This page provides a comparison between the Java and Scala programming languages by sharing side-by-side examples of each language. It’s intended for programmers who know Java and want to learn about Scala, specifically by seeing how Scala features compare to Java.
Overview
Before getting into the examples, this first section provides a relatively brief introduction and summary of the sections that follow. It presents the similarities and differences between Java and Scala at a high level, and then introduces the differences you’ll experience every day as you write code.
High level similarities
At a high level, Scala shares these similarities with Java:
- Scala code is compiled to .class files, packaged in JAR files, and runs on the JVM
- It’s an object-oriented programming (OOP) language
- It’s statically typed
- Both languages have support for lambdas and higher-order functions
- They can both be used with IDEs like IntelliJ IDEA and Microsoft VS Code
- Projects can be built with build tools like Gradle, Ant, and Maven
- It has terrific libraries and frameworks for building server-side, network-intensive applications, including web server applications, microservices, machine learning, and more (see the “Awesome Scala” list)
- Both Java and Scala can use Scala libraries:
- They can use the Akka actors library to build actor-based concurrent systems, and Apache Spark to build data-intensive applications
- They can use the Play Framework to develop server-side applications
- You can use GraalVM to compile your projects into native executables
- Scala can seamlessly use the wealth of libraries that have been developed for Java
High level differences
Also at a high level, the differences between Java and Scala are:
- Scala has a concise but readable syntax; we call it expressive
- Though it’s statically typed, Scala often feels like a dynamic language
- Scala is a pure OOP language, so every object is an instance of a class, and symbols like
+
and+=
that look like operators are really methods; this means that you can create your own operators - In addition to being a pure OOP language, Scala is also a pure FP language; in fact, it encourages a fusion of OOP and FP, with functions for the logic and objects for modularity
- Scala has a full suite of immutable collections, including
List
,Vector
, and immutableMap
andSet
implementations - Everything in Scala is an expression: constructs like
if
statements,for
loops,match
expressions, and eventry
/catch
expressions all have return values - Scala idioms favor immutability by default: you’re encouraged to use immutable (
final
) variables and immutable collections - Idiomatic Scala code does not use
null
, and thus does not suffer fromNullPointerException
- The Scala ecosystem has other build tools in sbt, Mill, and others
- In addition to running on the JVM, the Scala.js project lets you use Scala as a JavaScript replacement
- The Scala Native project adds low-level constructs to let you write “systems” level code, and also compiles to native executables
Programming level differences
Finally, these are some of the differences you’ll see every day when writing code:
- Scala’s syntax is extremely consistent
- Variables and parameters are defined as
val
(immutable, likefinal
in Java) orvar
(mutable) - Type inference makes your code feel dynamically typed, and helps to keep your code brief
- In addition to simple
for
loops, Scala has powerfulfor
comprehensions that yield results based on your algorithms - Pattern matching and
match
expressions will change the way you write code - Writing immutable code by default leads to writing expressions rather than statements; in time you see that writing expressions simplifies your code (and your tests)
- Toplevel definitions let you put method, field, and other definitions anywhere, also leading to concise, expressive code
- You can create mixins by “mixing” multiple traits into classes and objects (traits are similar to interfaces in Java 8 and newer)
- Classes are closed by default, supporting Joshua Bloch’s Effective Java idiom, “Design and document for inheritance or else forbid it”
- Scala’s contextual abstractions and term inference provide a collection of features:
- Extension methods let you add new functionality to closed classes
- Given instances let you define terms that the compiler can synthesize at using points, making your code less verbose and essentially letting the compiler write code for you
- Multiversal equality lets you limit equality comparisons—at compile time—to only those comparisons that make sense
- Scala has state of the art, third-party, open source functional programming libraries
- Scala case classes are like records in Java 14; they help you model data when writing FP code, with built-in support for concepts like pattern matching and cloning
- Thanks to features like by-name parameters, infix notation, optional parentheses, extension methods, and higher-order functions, you can create your own “control structures” and DSLs
- Scala files do not have to be named according to the classes or traits they contain
- Many other goodies: companion classes and objects, macros, union and intersection, numeric literals, multiple parameter lists, default values for parameters, named arguments, and more
Features compared with examples
Given that introduction, the following sections provide side-by-side comparisons of Java and Scala programming language features.
OOP style classes and methods
This section provides comparisons of features related to OOP-style classes and methods.
Comments:
//
|
//
|
OOP style class, primary constructor:
Scala doesn’t follow the JavaBeans standard, so instead of showing Java code written in the JavaBeans style, here we show Java code that is equivalent to the Scala code that follows it.
class Person {
|
class Person (
|
Auxiliary constructors:
public class Person {
|
class Person (
|
Classes closed by default:
“Plan for inheritance or else forbid it.”
final class Person
|
class Person
|
A class that’s open for extension:
class Person
|
open class Person
|
One-line method:
public int add(int a, int b) {
|
def add(a: Int, b: Int): Int = a + b
|
Multiline method:
public void walkThenRun() {
|
def walkThenRun() =
|
Immutable fields:
final int i = 1;
|
val i = 1
|
Mutable fields:
int i = 1;
|
var i = 1
|
Interfaces, traits, and inheritance
This section compares Java interfaces to Scala traits, including how classes extend interfaces and traits.
Interfaces/traits:
public interface Marker {};
|
trait Marker
|
Simple interface:
public interface Adder {
|
trait Adder:
|
Interface with a concrete method:
public interface Adder {
|
trait Adder:
|
Inheritance:
class Dog extends Animal implements HasLegs, HasTail
|
class Dog extends Animal, HasLegs, HasTail
|
Extend multiple interfaces
These interfaces and traits have concrete, implemented methods (default methods):
interface Adder {
|
trait Adder:
|
Mixins:
N/A |
class DavidBanner
|
Control structures
This section compares control structures in Java and Scala.
if
statement, one line:
if (x == 1) { System.out.println(1); }
|
if x == 1 then println(x)
|
if
statement, multiline:
if (x == 1) {
|
if x == 1 then
|
if, else if, else:
if (x < 0) {
|
if x < 0 then
|
if
as the method body:
public int min(int a, int b) {
|
def min(a: Int, b: Int): Int =
|
Return a value from if
:
Called a ternary operator in Java:
int minVal = (a < b) ? a : b;
|
val minValue = if a < b then a else b
|
while
loop:
while (i < 3) {
|
while i < 3 do
|
for
loop, single line:
for (int i: ints) {
|
//preferred
|
for
loop, multiple lines:
for (int i: ints) {
|
for
|
for
loop, multiple generators:
for (int i: ints1) {
|
for
|
Generator with guards (if
) expressions:
List ints =
|
for
|
for
comprehension:
N/A |
val list =
|
switch/match:
String monthAsString = "";
|
val monthAsString = day match
|
switch/match, multiple conditions per case:
String numAsString = "";
|
val numAsString = i match
|
try/catch/finally:
try {
|
try
|
Collections classes
This section compares the collections classes that are available in Java and Scala.
Immutable collections classes
Examples of how to create instances of immutable collections.
Sequences:
List strings = List.of("a", "b", "c");
|
val strings = List("a", "b", "c")
|
Sets:
Set set = Set.of("a", "b", "c");
|
val set = Set("a", "b", "c")
|
Maps:
Map map = Map.of(
|
val map = Map(
|
Mutable collections classes
Scala has mutable collections classes like ArrayBuffer
, Map
, and Set
in its scala.collection.mutable package.
After importing them into the current scope, they’re created just like the immutable List
, Vector
, Map
, and Set
examples just shown.
Scala also has an Array
class, which you can think of as being a wrapper around the Java array
primitive type.
A Scala Array[A]
maps to a Java A[]
, so you can think of this Scala Array[String]
:
val a = Array("a", "b")
as being backed by this Java String[]
:
String[] a = {"a", "b"};
However, a Scala Array
also has all of the functional methods you expect in a Scala collection, including map
and filter
:
val nums = Array(1, 2, 3, 4, 5)
val doubledNums = nums.map(_ * 2)
val filteredNums = nums.filter(_ > 2)
Because the Scala Array
is represented in the same way as the Java array
, you can easily use Java methods that return arrays in your Scala code.
Despite that discussion of
Array
, bear in mind that often in Scala there are alternatives toArray
that might be better suited. Arrays are useful for interoperating with other languages (Java, JavaScript) and they may also be useful when writing low-level code that needs to squeeze maximum performance out of the underlying platform. But in general, when you need to use a sequence, the Scala idiom is to prefer immutable sequences likeVector
andList
, and then useArrayBuffer
if and when when you really need a mutable sequence.
You can also convert between Java and Scala collections classes with the Scala CollectionConverters
objects.
There are two objects in different packages, one for converting from Java to Scala, and another for converting from Scala to Java.
This table shows the possible conversions:
Java | Scala |
---|---|
java.util.Collection | scala.collection.Iterable |
java.util.List | scala.collection.mutable.Buffer |
java.util.Set | scala.collection.mutable.Set |
java.util.Map | scala.collection.mutable.Map |
java.util.concurrent.ConcurrentMap | scala.collection.mutable.ConcurrentMap |
java.util.Dictionary | scala.collection.mutable.Map |
Methods on collections classes
With the ability to treat Java collections as streams, Java and Scala now have many of the same common functional methods available to them:
map
filter
forEach
/foreach
findFirst
/find
reduce
If you’re used to using these methods with lambda expressions in Java, you’ll find it easy to use the same methods on Scala’s collection classes.
Scala also has dozens of other collections methods, including head
, tail
, drop
, take
, distinct
, flatten
, and many more.
At first you may wonder why there are so many methods, but after working with Scala you’ll realize that because of these methods, you rarely ever need to write custom for
loops any more.
(This also means that you rarely need to read custom for
loops, as well.
Because developers tend to spend on the order of ten times as much time reading code as writing code, this is significant.)
Tuples
Java tuples are created like this:
Pair<String, Integer> pair =
new Pair<String, Integer>("Eleven", 11);
Triplet<String, Integer, Double> triplet =
Triplet.with("Eleven", 11, 11.0);
Quartet<String, Integer, Double, Person> quartet =
Quartet.with("Eleven", 11, 11.0, new Person("Eleven"));
Other Java tuple names are Quintet, Sextet, Septet, Octet, Ennead, Decade.
Tuples of any size in Scala are created by putting the values inside parentheses, like this:
val a = ("eleven")
val b = ("eleven", 11)
val c = ("eleven", 11, 11.0)
val d = ("eleven", 11, 11.0, Person("Eleven"))
Enums
This section compares enumerations in Java and Scala.
Basic enum:
enum Color {
|
enum Color:
|
Parameterized enum:
enum Color {
|
enum Color(val rgb: Int):
|
User-defined enum members:
enum Planet {
|
enum Planet(
|
Exceptions and error handling
This section covers the differences between exception handling in Java and Scala.
Java uses checked exceptions
Java uses checked exceptions, so in Java code you have historically written try
/catch
/finally
blocks, along with throws
clauses on methods:
public int makeInt(String s)
throws NumberFormatException {
// code here to convert a String to an int
}
Scala doesn’t use checked exceptions
The Scala idiom is to not use checked exceptions like this.
When working with code that can throw exceptions, you can use try
/catch
/finally
blocks to catch exceptions from code that throws them, but how you proceed from there is different.
The best way to explain this is that Scala code consists of expressions, which return values. As a result, you end up writing your code as a series of algebraic expressions:
val a = f(x)
val b = g(a,z)
val c = h(b,y)
This is nice, it’s just algebra. You create equations to solve small problems, and then combine equations to solve larger problems.
And very importantly—as you remember from algebra courses—algebraic expressions don’t short circuit—they don’t throw exceptions that blow up a series of equations.
Therefore, in Scala our methods don’t throw exceptions.
Instead, they return types like Option
.
For example, this makeInt
method catches a possible exception and returns an Option
value:
def makeInt(s: String): Option[Int] =
try
Some(s.toInt)
catch
case e: NumberFormatException => None
The Scala Option
is similar to the Java Optional
class.
As shown, if the string-to-int conversion succeeds, the Int
is returned inside a Some
value, and if it fails, a None
value is returned.
Some
and None
are subtypes of Option
, so the method is declared to return the Option[Int]
type.
When you have an Option
value, such as the one returned by makeInt
, there are many ways to work with it, depending on your needs.
This code shows one possible approach:
makeInt(aString) match
case Some(i) => println(s"Int i = $i")
case None => println(s"Could not convert $aString to an Int.")
Option
is commonly used in Scala, and it’s built into many classes in the standard library.
Other similar sets of classes like Try/Success/Failure and Either/Left/Right offer even more flexibility.
For more information on dealing with errors and exceptions in Scala, see the Functional Error Handling section.
Concepts that are unique to Scala
That concludes are comparison of the Java and Scala languages.
There are other concepts in Scala which currently have no equal in Java 11. This includes:
- Everything related to Scala’s contextual abstractions
- Several Scala method features:
- Multiple parameter lists
- Default parameter values
- Using named arguments when calling methods
- Case classes (like “records” in Java 14), case objects, and companion classes and objects (see the Domain Modeling) chapter
- The ability to create your own control structures and DSLs
- Toplevel definitions
- Pattern matching
- Advanced features of
match
expressions - Type lambdas
- Trait parameters
- Opaque type aliases
- Multiversal equality
- Type classes
- Infix methods
- Macros and metaprogramming