Core Language Concepts Interviewers Probe
A near-universal opener is 'what's the difference between a class, an object, and a case class?' — the expected answer covers that object defines a singleton, Scala's replacement for Java's static, a plain class needs explicit equals/hashCode/toString and a new keyword to instantiate, while a case class gets all of that generated automatically plus pattern-matching support and a copy method, at the cost of being effectively an immutable data-carrier by convention. Interviewers also frequently ask about trait vs abstract class: both can hold abstract and concrete members, but a class can extend multiple traits, Scala's answer to multiple inheritance resolved via linearization, while it can extend only one abstract class, and traits, unlike abstract classes in Scala 2, can't take constructor parameters until Scala 3 relaxed that restriction.
Cricket analogy: It's like an interviewer asking you to distinguish a franchise's single official captain, a singleton object with one instance, from any regular player who needs to be selected and kitted out individually, new-ing up a class — a strong candidate explains that a case class arrives pre-fitted with equipment, equals/hashCode/copy, a plain class doesn't.
Collections and Functional Programming Questions
A classic follow-up is explaining map vs flatMap: map applies a function A => B and keeps the same container shape, List[A] => List[B], while flatMap applies a function A => F[B] and then flattens one level of nesting, avoiding a List[List[B]], which is exactly the operation that powers for-comprehensions — a candidate who can show that for { x <- xs; y <- ys } yield (x, y) desugars to xs.flatMap(x => ys.map(y => (x, y))) demonstrates real understanding rather than memorized syntax. Interviewers also test lazy evaluation: a lazy val defers computation until first access and then caches the result, LazyList, Scala 3's renamed Stream, computes elements on demand and can represent infinite sequences like LazyList.from(1).filter(_ % 7 == 0), and understanding when a computation actually runs, strict collections evaluate eagerly while views and LazyList don't, is a common source of subtle bugs interviewers like to probe with a 'what does this print, and in what order?' question.
Cricket analogy: It's like explaining the difference between converting a list of players to their batting averages one-for-one, map, same shape, versus expanding each player into their entire list of career centuries and flattening it into one combined list, flatMap — a strong candidate can trace through both without hesitation.
Concurrency and Advanced Topics
Future[A] represents an async computation that's already running, or scheduled, on an implicit ExecutionContext, whereas a Promise[A] is the writable side you complete manually, letting you bridge callback-based APIs into Future-land by calling promise.success(value) when the callback fires and handing callers promise.future; interviewers use this to check whether you understand that Future is read-only from the consumer's side. Variance is another perennial topic: List[+A] is covariant, meaning List[Cat] is a subtype of List[Animal], which is sound because List is immutable and only ever produces As; a mutable Array[A] in Java is unsoundly covariant, leading to ArrayStoreException, while Scala's Array sidesteps this by being invariant, and contravariance, -A as in Function1[-A, +B]'s parameter position, lets a Function1[Animal, String] be substituted where a Function1[Cat, String] is expected because it can safely handle anything a Cat-accepting function could.
Cricket analogy: It's like explaining a DRS review already in progress with the third umpire, Future, already running, versus the on-field umpire's raised finger being the manual trigger that eventually confirms the outcome, Promise.success completing it — a good candidate distinguishes 'already computing' from 'the thing that resolves it.'
// Classic interview question: implement word count without groupBy/groupMapReduce
def wordCount(words: List[String]): Map[String, Int] =
words.foldLeft(Map.empty[String, Int]) { (acc, word) =>
acc.updated(word, acc.getOrElse(word, 0) + 1)
}
wordCount(List("scala", "java", "scala", "kotlin", "scala"))
// Map("scala" -> 3, "java" -> 1, "kotlin" -> 1)
// Follow-up: implement it tail-recursively without foldLeft
import scala.annotation.tailrec
def wordCountRec(words: List[String]): Map[String, Int] = {
@tailrec
def loop(remaining: List[String], acc: Map[String, Int]): Map[String, Int] =
remaining match {
case Nil => acc
case head :: tail =>
loop(tail, acc.updated(head, acc.getOrElse(head, 0) + 1))
}
loop(words, Map.empty)
}A common trap in this question: candidates reach for a var accumulator and a while loop out of habit. Walking the interviewer through the immutable foldLeft version first, then the @tailrec version, signals you understand both the functional idiom and how it compiles down to an efficient loop.
- case class vs class vs object: know what's auto-generated and when each is appropriate.
- trait vs abstract class: multiple trait mixins are resolved via linearization; only one abstract class can be extended.
- map keeps the same shape one-to-one; flatMap flattens one level after applying a function that returns a container.
- for-comprehensions with yield desugar to map/flatMap/withFilter — know this to debug 'why doesn't this compile' errors.
- Future is a read-only handle to an already-running computation; Promise is the writable side that completes it.
- Variance: List[+A] is safely covariant because it's immutable; Array is invariant in Scala to avoid Java's unsoundness.
- lazy val and LazyList defer computation — know when something actually executes to avoid subtle ordering bugs.
Practice what you learned
1. What does a case class provide automatically that a plain class does not?
2. Why can a class extend multiple traits but only one abstract class?
3. What is the key difference between map and flatMap on a List?
4. In Future/Promise, which statement is correct?
5. Why is Scala's List[+A] covariance considered sound while a Java-style mutable covariant array is not?
Was this page helpful?
You May Also Like
Scala vs Java
A practical comparison of Scala and Java covering syntax, type systems, concurrency, and when to choose one over the other on the JVM.
Scala Best Practices
Idiomatic patterns for writing clean, maintainable, and performant Scala code, from immutability to error handling to project structure.
Scala Quick Reference
A condensed cheat sheet of core Scala syntax — variables, collections, pattern matching, and common idioms — for quick lookup while coding.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics