title | type | description | num | previous-page | next-page |
---|---|---|---|---|---|
Scala for Java Developers |
chapter |
This page is for Java developers who are interested in learning about Scala 3. |
74 |
interacting-with-java |
scala-for-javascript-devs |
{% include_relative scala4x.css %}
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.
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.
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][modeling-oop] (OOP) language
- It’s statically typed
- Both languages have support for lambdas and [higher-order functions][hofs]
- 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
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][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
{% comment %} These are several notes that came up early in the writing process, and I (Alvin) can’t really address them: TODO: Need a good, simple way to state that Scala has a sound type system TODO: Points to make about Scala’s consistency? TODO: Add a point about how the type system lets you express details as desired {% endcomment %}
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][toplevel] 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][contextual] and term inference provide a collection of features:
- [Extension methods][extension-methods] let you add new functionality to closed classes
- [Given instances][givens] 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][multiversal] 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][hofs], 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][union-types] and [intersection][intersection-types], numeric literals, multiple parameter lists, default values for parameters, named arguments, and more
Given that introduction, the following sections provide side-by-side comparisons of Java and Scala programming language features.
This section provides comparisons of features related to OOP-style classes and methods.
//
|
//
|
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 (
|
public class Person {
|
class Person (
|
“Plan for inheritance or else forbid it.”
final class Person
|
class Person
|
class Person
|
open class Person
|
public int add(int a, int b) {
|
def add(a: Int, b: Int): Int = a + b
|
public void walkThenRun() {
|
def walkThenRun() =
|
final int i = 1;
|
val i = 1
|
int i = 1;
|
var i = 1
|
This section compares Java interfaces to Scala traits, including how classes extend interfaces and traits.
public interface Marker;
|
trait Marker
|
public interface Adder {
|
trait Adder:
|
public interface Adder {
|
trait Adder:
|
class Dog extends Animal implements HasLegs, HasTail
|
class Dog extends Animal, HasLegs, HasTail
|
These interfaces and traits have concrete, implemented methods (default methods):
interface Adder {
|
trait Adder:
|
N/A |
class DavidBanner
|
This section compares [control structures][control] in Java and Scala.
if (x == 1) { System.out.println(1); }
|
if x == 1 then println(x)
|
if (x == 1) {
|
if x == 1 then
|
if (x < 0) {
|
if x < 0 then
|
public int min(int a, int b) {
|
def min(a: Int, b: Int): Int =
|
Called a ternary operator in Java:
int minVal = (a < b) ? a : b;
|
val minValue = if a < b then a else b
|
while (i < 3) {
|
while i < 3 do
|
for (int i: ints) {
|
//preferred
|
for (int i: ints) {
|
for
|
for (int i: ints1) {
|
for
|
List ints =
|
for
|
N/A |
val list =
|
String monthAsString = "";
|
val monthAsString = day match
|
String numAsString = "";
|
val numAsString = i match
|
try {
|
try
|
This section compares the [collections classes][collections-classes] that are available in Java and Scala.
Examples of how to create instances of immutable collections.
List strings = List.of("a", "b", "c");
|
val strings = List("a", "b", "c")
|
Set set = Set.of("a", "b", "c");
|
val set = Set("a", "b", "c")
|
Map map = Map.of(
|
val map = Map(
|
Scala has mutable collections classes like ArrayBuffer
, Map
, and Set
in its scala.collection.mutable package.
After [importing them][imports] 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 |
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][collections-classes].
Scala also has dozens of other [collections methods][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.)
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> triplet =
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"))
This section compares enumerations in Java and Scala.
enum Color {
|
enum Color:
|
enum Color {
|
enum Color(val rgb: Int):
|
enum Planet {
|
enum Planet(
|
This section covers the differences between exception handling in Java and Scala.
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
}
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][error-handling] section.
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][contextual]
- 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][modeling-intro]) chapter
- The ability to create your own control structures and DSLs
- [Toplevel definitions][toplevel]
- Pattern matching
- Advanced features of
match
expressions - Type lambdas
- Trait parameters
- [Opaque type aliases][opaque]
- [Multiversal equality][equality]
- [Type classes][type-classes]
- Infix methods
- Macros and metaprogramming
[collections-classes]: {% link _overviews/scala3-book/collections-classes.md %} [collections-methods]: {% link _overviews/scala3-book/collections-methods.md %} [control]: {% link _overviews/scala3-book/control-structures.md %} [equality]: {% link _overviews/scala3-book/ca-multiversal-equality.md %} [error-handling]: {% link _overviews/scala3-book/fp-functional-error-handling.md %} [extension-methods]: {% link _overviews/scala3-book/ca-extension-methods.md %} [givens]: {% link _overviews/scala3-book/ca-given-using-clauses.md %} [hofs]: {% link _overviews/scala3-book/fun-hofs.md %} [imports]: {% link _overviews/scala3-book/packaging-imports.md %} [modeling-intro]: {% link _overviews/scala3-book/domain-modeling-intro.md %} [modeling-oop]: {% link _overviews/scala3-book/domain-modeling-oop.md %} [opaque]: {% link _overviews/scala3-book/types-opaque-types.md %} [tools]: {% link _overviews/scala3-book/scala-tools.md %} [toplevel]: {% link _overviews/scala3-book/taste-toplevel-definitions.md %} [type-classes]: {% link _overviews/scala3-book/ca-type-classes.md %}
[concurrency]: {% link _overviews/scala3-book/concurrency.md %} [contextual]: {% link _overviews/scala3-book/ca-contextual-abstractions-intro.md %} [control]: {% link _overviews/scala3-book/control-structures.md %} [fp-intro]: {% link _overviews/scala3-book/fp-intro.md %} [intersection-types]: {% link _overviews/scala3-book/types-intersection.md %} [modeling-fp]: {% link _overviews/scala3-book/domain-modeling-fp.md %} [multiversal]: {% link _overviews/scala3-book/ca-multiversal-equality.md %} [union-types]: {% link _overviews/scala3-book/types-union.md %}