100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Scala Interview Questions

Frequently asked Scala interview topics — from core language features to collections, concurrency, and functional programming concepts — with the reasoning interviewers look for.

PracticeIntermediate10 min readJul 10, 2026
Analogies

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.'

scala
// 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

Was this page helpful?

Topics covered

#Programming#ScalaStudyNotes#ScalaInterviewQuestions#Scala#Interview#Questions#Core#StudyNotes#SkillVeris#ExamPrep