Scala Tutorial for Java Developers
1. Introduction
In this lesson, we will look at some excellent examples with which Java programmers can get started with Scala very fast and learn some important concepts that will help them call themselves as Scala professionals in a very short time.
Well, the biggest challenge for any programmer, may he be comfortable with Java, Kotlin, Python or any other programming language is to learn another programming language. This is because when we are comfortable with a programming language, we tend to find the same qualities in the new programming language as well which, in most cases, presents disappointment because there is always something new in each programming language. Still, in this lesson, we will present an easy, clear to pick approach by comparing code-snippets which were in Java and their equivalent in Scala programming language. Some of the good things with Scala are:
- The Scala’s XML API is very good
- Type inference like Scala is very less verbose and more understandable than more explicit static typing (though that has its benefits too). And for a language that doesn’t require too much typing (compared to others), it performs well
- Pattern matching; case statements in Scala are super-powerful
- Inheritance due to mixing traits (more on this later) are great, and they definitely reduce code repetition
- Concurrency (actor model is far more digestible than threading model, IMO)
In some of the final sections, we will also cover how execution and compilation Scala code differs from that of Java programming language so that we can get a sense of completeness and our understanding with Scala is not just limited to the code comparison but how it executes on the JVM as well.
Table Of Contents
2. Hello World with Scala
As per the traditional methods, we can start working in Scala by executing a “Hello World” program quickly. Here is a Java program for printing “Hello World” to the console:
Hello World with Java
class HelloWorld { public static void main(String[] args) { System.out.println("Hello, geeks!"); } }
Now, if we write an equivalent Scala program, it will look very similar to that above:
Hello World with Scala
object HelloWorld { def main(args: Array[String]) { println("Hello, geeks!") } }
Here is the anatomy of the above program in terms of Java, which is very much similar to the original Java program. But we need to provide points about how it stands different from a Java program as well and that is exactly what we will do here:
- The most similar thing is the main method which is the entry point for this Scala program, just like Java. It takes input an array of String arguments and contains a single line of code, a call to the pre-defined method with a String greeting
- Instead of the
public void static
definition for the main method, we usedef
keyword to define a Scala function.Also, there is nothing like static in Scala (more on static members in later section) - The part which might not make sense to Java programmers will be the
object
definition in the beginning. This definition defines a class called HelloWorld and an instance with the same name. This instance is created on demand, i.e. when it is used the first time
To run the program, we are using an online compiler known as Scastie which is an excellent tool to run Scala programs without having to go through the process of installing and running Scala on your local machine. Here is the output we get when we run the above Scala program:
Let’s continue our journey by looking at some other important conceptual differences on how Scala stands differently from Java. Going forward in this lesson, we will make use of the IntelliJ IDE to write and run programs for Scala with a plugin which is easy to install.
3. Using Java classes in Scala
The most important thing to understand with Scala is that we can use everything from Java packages in our Scala programs. To make it more clear, let us look at a clear example by using java.io.File package from Java which creates a file and print its absolute path:
Using Java’s File package in Scala
package com.javacodegeeks import java.io.File object FileObject { def main(args: Array[String]): Unit = { val file = new File("JCG.txt") print(file getAbsolutePath) } }
To keep our files in a Scala project, we have made a new project and added our files in it. Here is the project structure we are using for now:
Once we run our program, we will see the absolute path of the file being printed on the console. Just like Java, the java.lang
package is imported by default in a Scala program. Also, the import statement is similar to Java but it is more powerful as it is possible to import multiple classes from a single package to be imported in a single statement. Let us look at an example for the syntax we use in this case:
Multiple imports in a single line
import java.util.{Date, Locale}
This representation of importing multiple classes from a single package in a single statement makes code a lot more consise and easy to read. Finally, instead of using an asterisk (*), we use an underscore (_) to import all classes of a package because asterisk is a valid Scala identifier (e.g. method name).
Next thing to notice is that print statement. The syntax file getAbsolutePath
is equivalent to file.getAbsolutePath()
code in Java and it represents in infix syntax property of Scala. If getAbsolutePath
method can take 1 argument, we could have even wrote it like:
Infix syntax in Scala
file getAbsolutePath true
It’s equivalent code in Java would have been:
Java equivalent
file.getAbsolutePath(true);
This can be done when a method takes just one argument as an input. Of course, the above expression is just for demonstration purposes as getAbsolutePath method doesn’t actually take an argument as an input.
Another thing to note in the above program is the use of val
keyword. Scala is a pure object-oriented programming language and like Java, it does not make use of primitive types. This means, that everything is an Object in Scala.
To conclude this section about integration with Java, it should be noted that it is also possible to inherit from Java classes and implement Java interfaces directly in Scala. This means that the complete Java class set is at disposal in a Scala program but it also leads to the performance overhead as we will see in a later section when we compare Java performance to Scala.
4. Classes in Scala
Based on the standard Java definition, a Class is a blueprint to create an Object. Here is the minimal class definition in Scala:
Minimal class definition
class Student var shubham = new Student
This class definition contains just the class definition and the variable identifier as the class object. To expand upon this example, it is possible to include a standard class constructor in the class definition itself in Scala:
Class definition with constructor
package com.javacodegeeks class Student(var name: String, var rollNo: Int) { def getName: String = this.name override def toString: String = s"($name, ', ', $rollNo)" }
Apart from the complete class definition with inline constructor, we also provided an example on how to provide a getter and override a function in Scala with the override
keyword in which we concatenate Strings as well.
To return a value from a function in Scala, we must specify its return type after function definition as described in the getName
function. Next, we override the toString
function, in which we concatenate Strings with the help of $
operator. We can make use of the class we defined above and make its object. Let is demonstrate this with an example:
Making Class object
package com.javacodegeeks object HelloWorld { def main(args: Array[String]) { val shubham = new Student("Shubham", 1) println(shubham.getName) } }
Simply, we provided default values to the object we are making and using val
keyword to create a new instance of the Student
class.
5. Functional Programming in Scala
Just like everything else, functions are objects in Scala. It is, therefore, possible to pass functions as arguments to other functions, to store them in variables, and to return them from other functions as well. This allows us to manipulate functions just like values and it is one of the definitions of a very interesting programming paradigm called functional programming.
In this section, we will look at a simple example where we pass a function as an argument to another function. To add, we will pass another boolean variable to the main function so that we can invoke the passed function only conditionally. Here is the code snippet for the example:
Functions as arguments
package com.javacodegeeks object FunctionArgs { def checkIfStudent(callback: () => Unit, shouldCheck: Boolean): Unit = { if(shouldCheck) callback() } def printStudent(): Unit = { println("I am a Student.") } def main(args: Array[String]): Unit = { checkIfStudent(printStudent, shouldCheck = true) } }
The function we defined first is the checkIfStudent
and it gets a call-back function as argument. The type of this function is written () => Unit and is the type of all functions which take no arguments and return nothing (the type Unit is similar to void in C/C++). The main function of this program simply calls this checkIfStudent function with a call-back which prints a sentence on the terminal.
6. Inheritance and Overriding in Scala
All classes in Scala inherit from a super-class. When no super-class is specified, as in the Student
example of previous section, scala.AnyRef
is implicitly used.
It is possible to override a method in Scala from one of its parent class but describing this is mandatory by using the override
keyword. This can be seen from one of the examples we earlier defined:
Overidding toString method
package com.javacodegeeks class Student(var name: String, var rollNo: Int) { ... override def toString: String = s"($name, ', ', $rollNo)" }
Here, we overridden a function provided by the super class. Let’s look at a quick example on how to extend a class in Scala. We will define a base class first:
Scala Base class
package com.javacodegeeks.inheritance abstract class Pizza(price: Double) { def getPrice: Double = price }
Next, we can define a child class which will extend the above abstract class and implementing necessary abstract methods:
pom.xml
package com.javacodegeeks.inheritance class FarmhousePizza(veggies: String, price: Double) extends Pizza(price) { override def getPrice: Double = super.getPrice }
Finally, we can make an object in which we make the instance of FarmhousePizza:
pom.xml
package com.javacodegeeks.inheritance object FarmhouseObj { def getOnePizza(veggies: String): Pizza = { new FarmhousePizza(veggies, 25.2) } }
This way, we can make a child class with more properties than a parent class and make use of inheritance. In Scala, there are five types of inheritance supported:
- Single-level Inheritance: When one class inherits from a single other class, it is termed as Single-level Inheritance
- Multilevel Inheritance: When one class extends another, which in turn extends another, it is an example of multilevel inheritance
- Multiple Inheritance: When one class inherits from multiple base classes, it is a case of multiple inheritances. Let us describe it diagramatically:
7. Traits in Scala
Traits in Scala are very much similar to interfaces in Java. They can be used define a functionality without actual implementation (or with implementation). A Scala class can make use of any number of Traits. Here are basic properties of a Trait:
- Traits define an object by specifying the signature of the methods it supports.
- Traits are declared in the same way as class except that the keyword
trait
is used
For quick demonstration, let us look at a simple example for explaining Traits:
Traits in Scala
trait Pizza { def price(type: String): Double } class Farmhouse extends Pizza { override def price(source:String) = { .. logic to get price .. } } object mypizza { def main(args:Array[String]){ val p1 = new Farmhouse println(p1.price("Cheese")) println(p1.isInstanceOf[Pizza]) // display true } }
We are defining a trait Pizza without any method implementation. In the class Farmhouse we are overriding the method price and then creating an object mypizza
. A Trait is often used when we need to implement the reusable behaviour in our code. In this context, the choice between an abstract class and a Trait is not very much evident. Here are some pointers which can be used to make this decision:
- If we consider performance, an abstract class is preferred over a Trait as a Trait is ultimately compiled to an interface which might be an overhead to performance.
- If we want to inherit from a Java interface, it is advisable to use a class again. This is because Traits do not exist in Java and it might not be readable enough.
- Traits are recommended to be used if classes inheriting a behavior are completely unrelated. This means that abstract classes are usually used when we have a parent concept. Traits should not be looked at in the same way and they are just behavior, not complete inheritance.
Usually, Traits are found difficult to understand but theyr are explained here with very simple exmaple and intuitive comparison to Java features to make it easy to catch.
8. Genericity in Scala
Genericity in Scala is just like Generics in Java. Just like Java, the generics facility introduced the concept of the type variable. A type variable, according to the Java Language Specification, is an unqualified identifier introduced by:
- Generic class declarations
- Generic interface declarations
- Generic method declarations
- Generic constructor declarations.
Generic classes in Scala take a type as a parameter within square brackets []
. One convention (read not mandatory) is to use the letter A as type parameter identifier, though any parameter name may be used:
Generics in Scala
class Stack[A] { private var elements: List[A] = Nil def push(x: A) { elements = x :: elements } def peek: A = elements.head def pop(): A = { val currentTop = peek elements = elements.tail currentTop } }
Above is an implementation of a Stack data structure implemented as a Generic class which can accept type as a parameter. If we want to implement a Stack of String, we can do so as:
Stack of String
val stack = new Stack[String] stack.push("Hello") stack.push("World") println(stack.pop) // prints World println(stack.pop) // prints Hello
This way, a new class instance can be made and used with another type.
9. Execution model in Scala
The Scala compiler is more sophisticated than Java’s, providing type inference, implicit conversion, and a much more powerful type system. These features don’t come for free, so I wouldn’t expect scalac (the Scala compiler) to ever be as fast as javac. This reflects a trade-off between the programmer doing the work and the compiler doing the work. That said, we can point some key overheads which a Scala program has to bear which shows a lower performance than a Java equivalent:
- Scalac itself consists of a LOT of classes which have to be loaded and jit-compiled
- Scalac has to search the classpath for all root packages and files. Depending on the size of your classpath this can take one to three extra seconds.
- Type inference is costly, in particular if it involves implicit search.
- Scalac has to do type checking twice; once according to Scala’s rules and a second time after erasure according to Java’s rules.
- Besides type checking there are about 15 transformation steps to go from Scala to Java, which all take time.
- Scala typically generates many more classes per given file size than Java, in particular, if functional idioms are heavily used. Bytecode generation and class writing takes time.
On the other hand, a 1000 line Scala program might correspond to a 2-3K line Java program, so some of the slower speed when counted in lines per second has to balanced against more functionality per line.
10. Conclusion
In this lesson, we explored Scala syntax and how it compared to Java syntax when it comes to functions, defining variables and classes, making constructors, inheriting existing classes and some new concepts called Traits and Genericity in Scala. It is important to understand that Scala might not be an exact replacement for Java and it doesn’t intend to do that (not willing, at least). Concurrency is becoming ever-more important. Already we have 1000 core processors being demonstrated, and what computer nowadays doesn’t already come with a GPU capable of highly-parallel operations? In order to take advantage of future machines, your software has to be able to make use of all this concurrency, and Java’s current threading primitives just aren’t going to help here. Let’s face it, concurrent programming under current mainstream paradigms is crazy-hard.
Scala is a functional language. As such it fully embraces concepts such as immutability, for-comprehensions, closures, etc. It also has 4 native actor libraries available (at last count). All of this is perfectly suited to the next generation of concurrent programming. You can’t bury your head in the sand and pretend that none of this exists – that would be like writing a program in 1995 that only stores 2-digit years – imperative loops simply aren’t going to cut it anymore! Java was introduced in 1995. So it was designed like other conventional programming languages. But it was subsequently upgraded with functional programming capability. The programmers can use Java 8 to write functional programs efficiently. But Scala was designed from the beginning as a functional programming language. Unlike Java, it has much better and advanced functional programming features like monads, immutability, and concurrency.
If you want a platform to practice Scala programs, I highly recommend Scala exercises to understand important concepts in Scala by solving code errors and writing fresh code. Finally, we only pointed out and explained some very important concepts in this lesson and missed out some others (loops, conditionals etc.) intentionally as they work extremely similar to that of Java so they do not need their section here. If you think a section should added and is worth explaining, please add it in comments below.
11. Download the Source Code
This lesson was a Scala Tutorial for Java Developers.
You can download the full source code of this example here: Scala Example
Traits in Scala are very much similar to interfaces in Java. -> This statement is wrong
Traits in Scala are very much similar to abstract classes in Java not Interfaces.