Scala Interview Questions

Prepare better with the best interview questions and answers, and walk away with top interview tips. These interview questions and answers will boost your core interview skills and help you perform better. Be smarter with every interview.

  • 4.5 Rating
  • 30 Question(s)
  • 32 Mins of Read
  • 4875 Reader(s)

Beginner

FlatMap is a transformation operation in Apache Spark to create an RDD from existing RDD. It takes one element from an RDD and can produce 0, 1 or many outputs based on business logic. It is similar to Map operation, but Map produces one to one output. If we perform Map operation on an RDD of length N, output RDD will also be of length N. But for FlatMap operation output RDD can be of different length based on business logic

val array1d = Array(“Hello,World”, “This,is,an,example”)
//array1d is an array of strings
val maped_array = array1d.map(x => x.split(“,”))
//maped_array will be: Array(Array(Hello, World), Array(This, is, an, example))
val flatMap_array = array1d.flatMap(x => x.split(“,”))
//flatMap_array will be: Array(Hello, World, This, is, an, example)
val salaries = Seq(20000, 70000, 40000)
val doubleSalary = (x : Int) => x*2
val newSalary = salaries.map(doubleSalary)
>List(40000, 140000, 80000)

Or

val salaries = Seq(20000, 70000, 40000)
val newSalary = salaries.map(x => x*2)
//List(40000, 140000, 80000)

For any function, just create an RDD and call toDebugString, for example:

scala> val a = sc.parallelize(Array(1,2,3)).distinct
scala> a.toDebugString
MappedRDD[5] at distinct at <console>:12 (1 partitions)
 MapPartitionsRDD[4] at distinct at <console>:12 (1 partitions)
**ShuffledRDD[3] at distinct at <console>:12 (1 partitions)**
  MapPartitionsRDD[2] at distinct at <console>:12 (1 partitions)
    MappedRDD[1] at distinct at <console>:12 (1 partitions)
       MappedRDD[1] at distinct at <console>:12 (1 partitions)

As you can see distinct creates a shuffle. It is also particularly important to find out this way rather than docs because there are situations where a shuffle will be required or not required for a certain function. For example, join usually requires a shuffle but if you join two RDDs that branch from the same RDD spark can sometimes elide the shuffle.

Singleton Object: Scala classes cannot have static variables or methods. Instead of a Scala class can have what is called a singleton object, or sometimes a companion object. A singleton object is declared using the object keyword. For example:

 object Main{
      def myfunc(){
          println(“Hello World”)   
         }
}

The above example defines a singleton object called Main. You can call the method my_func() like this:

Main.myfunc()

Companion Object: When a singleton object is named the same as a class, it is called a companion object. A companion object must be defined inside the same source file as the class. For example:

class Main{
      def myfunc(){
          println(“Hello World”)   
         }
     }
      object Main{
      Def mynewfunc(){
          println(“This is my first program”)
         }
    }

In this class you can both instantiate Main and call sayHelloWorld() or call the sayHi() method on the companion object directly, like this:

var aMain: Main = new Main();
aMain.myfunc();
Main.mynewfunc();

In Scala, when we create an instance of a Class without using ‘new’ keyword, internally it makes a call to the appropriate apply method available in Companion object. Here appropriate apply method means that matched with parameters.

There are two kinds of program evaluations:

  • Eager Evaluation
  • Lazy Evaluation

Eager Evaluation means evaluating the program at compile-time or program deployment-time irrespective of clients are using that program or not.

Lazy Evaluation means evaluating the program at run-time on-demand that means when clients access the program then only its evaluated.

Literal is value written exactly as it's meant to be interpreted. In contrast, a variable is a name that can represent different values during the execution of the program. And a constant is a name that represents the same value throughout a program. But a literal is not a name -- it is the value itself.

The different types of literals in Scala are

  • Integer literals
  • Floating point literals
  • Boolean literals
  • Symbol literals
  • Character literals
  • String literals
  • Multi-Line strings

A var is a variable. It’s a mutable reference to a value. Since it’s mutable, its value may change through the program lifetime.

On the other hand, val keyword represents a value. It’s an immutable reference, meaning that its value never changes. Once assigned it will always keep the same value. It’s similar to a final variable in Java or constants in other languages. It's also worth to remember that the variable type cannot change in Scala. You may say that a var behaves similarly to Java variables.

While the def is a function declaration. It is evaluated on call, similar to Python, where def is also used to declare a function.

According to the private access specifier, private members can be accessed only within that class but Scala’s companion object and class provide special access to private members. A companion object can access all the private members of a companion class. Similarly, a companion class can access all the private members of companion objects.

Java’s == operator is used to check References Equality that is whether two references are pointing to the same object or not. Scala’s == is used to check Instances Equality that is whether two instances are equal or not.

Pattern matching is a feature of Scala. It works same as switch case in other languages. It matches the best case available in the pattern.

object Main {  
def main(args: Array[String]) {  
var a = 1  
       a match{  
           case 1 => println("Hello")  
           case 2 => println("World")  
           case _ => println("!!!!")  
       }
       }
}

In Scala, for loop is known as for-comprehensions. It can be used to iterate, filter and return an iterated collection. The for-comprehension looks a bit like a for-loop in imperative languages, except that it constructs a list of the results of all iterations.

object MainObject {  
                 def main(args: Array[String]) {  
                 for( a <- 1 to 10 ){  
                         println(a);
                        }
                 }
          }

Seq is a trait which represents indexed sequences that are guaranteed immutable. You can access elements by using their indexes. It maintains insertion order of elements.

Sequences support many methods to find occurrences of elements or subsequences. It returns a list.

 scala> Seq(1, 1, 2)
      res3: Seq[Int] = List(1, 1, 2)

Scala Set is a collection of pairwise different elements of the same type. In other words, a Set is a collection that contains no duplicate elements. There are two kinds of Sets, the immutable and the mutable. The difference between mutable and immutable objects is that when an object is immutable, the object itself can't be changed.

By default, Scala uses the immutable Set. If you want to use the mutable Set, you'll have to import scala.collection.mutable.Set class explicitly. If you want to use both mutable and immutable sets in the same collection, then you can continue to refer to the immutable Set as Set but you can refer to the mutable Set as mutable.Set.

Here is how you can declare immutable Sets −

// Empty set of integer type
var s : Set[Int] = Set()

// Set of integer type
var s : Set[Int] = Set(1,3,5,7)

or

var s = Set(1,3,5,7)

For each iteration of for loop, yield generates a value which will be remembered. It's like the for loop has a buffer you can’t see, and for each iteration of for loop, another item is added to that buffer. When loop finishes running, it will return this collection of all the yielded values.

In Scala, you can create an anonymous object. An object which has no reference name is called an anonymous object. It is good to create an anonymous object when you don't want to reuse it further.

class Arithmetic{  
   def add(a:Int, b:Int){  
       var add = a+b;  
       println("sum = "+add);  
   }
}  
object MainObject{  
   def main(args:Array[String]){  
       new Arithmetic().add(10,10);  
   }
}

Scala Option[ T ] is a container for zero or one element of a given type. An Option[T] can be either Some[T] or None object, which represents a missing value. For instance, the get method of Scala's Map produces Some(value) if a value corresponding to a given key has been found, or None if the given key is not defined in the Map. Option type is used frequently in Scala programs.

It is used for representing whether a value is present or absent. Option collections can be used for wrapping missing values.

apply and unapply methods in Scala are used for mapping and unmapping data between form and model data.

Apply method – Used to assemble an object from its components. For example, if we want to create an Employee object then use the two components firstName and lastName and compose the Employee object using the apply method.

Unapply method – Used to decompose an object from its components. It follows the reverse process of the apply method. So if you have an employee object, it can be decomposed into two components- firstName and lastName.

Scala supports both call-by-value and call-by-name function parameters. However, Java supports only call-by-value, but not call-by-name.

Difference between call-by-value and call-by-name:

  • In Call-by-name, the function parameters are evaluated only whenever they are needed but not when the function is called.
  • In Call-by-value, the function parameters are evaluated when the function is called.
  • In Call-by-value, the parameters are evaluated before executing the function and they are evaluated only once irrespective of how many times we used them in that function.
  • In Call-by-name, the parameters are evaluated whenever we access them and they are evaluated each time we use them in that function.
  • Scala Syntax Differences

App is a trait defined in scala package as "scala.App" which defines the main method. If an object or class extends this trait then they will become Scala executable programs automatically as they inherit the main method from an application. Developers need not write the main method when using App but the only drawback of using App is that developers have to use same name args to refer command line arguments because of scala. App's main() method uses this name.

Advanced

A Diamond Problem is Multiple Inheritance problems. Some people call this problem as Deadly Diamond Problem. In Scala, it occurs when a Class extends more than one Traits which have same method definition.

Unlike Java 8, Scala solves this diamond problem automatically by following some rules defined in Language. Those rules are called “Class Linearization”.

trait A{   
 def display(){ println("From A.display")  }
}
trait B extends A{
 override def display() { println("From B.display") }
}
trait C extends A{
 override def display() { println("From C.display") }
}
class D extends B with C{ }
object ScalaDiamonProblemTest extends App {
   val d = new D
   d display
}

Here the output is “From C.display” form trait C. Scala Compiler reads “extends B with C” from right to left and takes “display” method definition from lest most trait that is C.

A monad is an object that wraps another object. You pass the Monad mini-programs, i.e functions, to perform the data manipulation of the underlying object, instead of manipulating the object directly.  Monad chooses how to apply the program to the underlying object.

Currying transforms a function that takes multiple parameters into a chain of functions, each taking a single parameter. Curried functions are defined with multiple parameter lists.

The advantage of Currying in Scala:

  1. One benefit is that Scala currying makes creating anonymous functions easier.
  2. Scala Currying also makes it easier to pass around a function as a first-class object. You can keep applying parameters when you find them.

Scala has two kinds of constructors:

  • Primary Constructor
  • Auxiliary Constructor

Primary Constructor: In Scala, Primary Constructor is a constructor which is defined with class definition itself. Each class must have one Primary Constructor: Either Parameter constructor or Parameterless constructor.

Example:-

class Person

Above Person class has one Zero-parameter or No-Parameter or Parameterless Primary constructor to create instances of this class.

class Person (firstName: String, lastName: String)

Above Person class has two Parameters, Primary constructor, to create instances of this class.

Auxiliary Constructor: Auxiliary Constructor is also known as Secondary Constructor. We can declare a Secondary Constructor using ‘def’ and ‘this’ keywords as shown below:

class Person (firstName: String, middleName:String, lastName: String){
 def this(firstName: String, lastName: String){
     this(firstName, "", lastName)
 }
}

Streams in Scala are a type of lazy collection, which are created using a starting element and then recursively generated using those elements. Streams are like a List, except that, elements are added only when they are accessed, hence “lazy”. Since streams are lazy in terms of adding elements, they can be unbounded also, and once the elements are added, they are cached. Since Streams can be unbounded, and all the values are computed at the time of access, programmers need to be careful on using methods which are not transformers, as it may result in java.lang.OutOfMemoryErrors.

stream.max

stream.size

stream.sum

The main reason to take this decision is to make Scala as a Pure Object-Oriented Language. “static” keyword means that we can access that class member without creating an object or without using an object. This is completely against with OOP principles.

If a Language supports “static” keyword, then that Language is not a Pure Object-Oriented Language. For instance, as Java supports “static” keyword, it is NOT a Pure Object-Oriented Language. But Scala is a Pure Object-Oriented Language.

Higher order function is a function that either takes a function as an argument or returns a function. In other words, we can say a function which works with function is called a higher-order function.

object MainObject {  
  def main(args: Array[String]) = {  
    functionExample(25, multiplyBy2 // Passing a function as parameter  
   }
   def functionExample(a:Int, f:Int=>AnyVal):Unit = {  
       println(f(a))       // Calling that function   
   }
   def multiplyBy2(a:Int):Int = {  
       a*2
   }
}

‘Traits’ are used to define object types specified by the signature of the supported methods.  Scala allows to be partially implemented but traits may not have constructor parameters. A trait consists of method and field definition, by mixing them into classes it can be reused.

trait Printable{  
   def print()  
}  
class A4 extends Printable{  
   def print(){  
       println("Hello")  
   }
}  
object Main{  
   def main(args:Array[String]){  
       var a = new A4()  
       a.print()  
   }
}

A Seq is an Iterable that has a defined order of elements. Sequences provide a method apply() for indexing, ranging from 0 up to the length of the sequence. Seq has many subclasses including Queue, Range, List, Stack, and LinkedList.

import scala.collection.immutable._  
object MainObject{  
   def main(args:Array[String]){  
       var seq:Seq[Int] = Seq(52,85,1,8,3,2,7)  
       seq.foreach((element:Int) => print(element+" "))  
       println("\nAccessing element by using index")  
       println(seq(2))  
   }
}

A List is a Seq that is implemented as an immutable linked list. It's best used in cases with last-in first-out (LIFO) access patterns.

import scala.collection.immutable._  
object MainObject{  
   def main(args:Array[String]){  
      var list = List(1,8,5,6,9,58,23,15,4)  
       var list2:List[Int] = List(1,8,5,6,9,58,23,15,4)  
       println(list)  
       println(list2)  
   }
}

A tail-recursive function is just a function whose very the last action is a call to itself. When you write your recursive function in this way, the Scala compiler can optimize the resulting JVM bytecode so that the function requires only one stack frame — as opposed to one stack frame for each level of recursion.

Description

Prepare better with the best interview questions and answers, and walk away with top interview tips. These interview questions and answers will boost your core interview skills and help you perform better. Be smarter with every interview.
Levels