Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / Java

10 Differences Between Scala and Java 8: Part 1

4.21/5 (12 votes)
3 Apr 2015CPOL18 min read 55.8K  
Due to their full runtime compatibility “Scala vs. Java” is a never-ending debate in Java development world.

Java and Scala comparison

Many Java developers absolutely love Scala and always prefer it over Java for any kind of tasks: new applications, components of existing ones or even performance-critical elements of existing Java modules. This makes Scala more and more popular, and it’s already made its way to an enterprise world.

Such known companies as Amazon and Blizzard are already using Scala and facilitate their apps with all the benefits it has. It was love from the first sight between Scala and us, and we just want to tell you why.

There are a lot of articles that made it quite clear that although somewhat complex upfront, the functional approach of Scala language multiplied by absolute Java interoperability makes it far superior to plain old Java 6 or Java 7.

But Scala has proved its usefulness to the point when new versions of Java started implementing Scala features, as well as other functional languages. By releasing Java 8, Oracle has caught up with the trend of pumping up conventional imperative OOP languages with functional programming features. This release of Java has incorporated some functional features of Scala that are the most popular among developers, the ones that lessen the routine of trivial operations in developer’s work:

  • Support for lambda expressions: no more tons of unnecessary code for common operations like working with collections;
  • Support for higher-order functions: make your code more reusable, much less literal and much more self-explanatory;
  • Parallel collections: enjoy some benefits of multi-core systems for data processing without any headache;
  • Type inference: make code more reusable and less literal, but still only on runtime level, though;
  • Virtual extension methods (as opposed to traits in Scala): it allows new behaviors for classes:

If you take a closer look, a lot of approaches that have been introduced in Java 8 are really similar to the ones offered by Scala. But is there still a difference, and should you choose Scala over Java anyway?

Here is our comparison of Scala and Java 8 which we’ve made as a list of distinctive features.

1. Even with Project Lambda, Java 8 is Still a Conventional Imperative Language

Though new features and approaches are surely simplifying developers’ life, it's still quite not the same as functional programming. Java 8 only gives a glimpse of what functional programming has to offer: the most basic stuff.

Still great stuff, nevertheless, it gets praised by beginners in Scala:

“You can do this stuff in one line!”

“You don't have to do that stuff anymore at all!”

“In Java this code would be a clunky disaster, in Scala it is as clear as a sunny day!”

Oracle did a good job of grasping and grabbing most of the hot features that functional approach has to offer, and in many cases implemented them nearly identical to Scala, so no one will ask “Everything was so good in Scala, why Java had to ruin it?”.

Somebody can even think “Well, Java has all the good stuff now, what more can I possibly get from switching to Scala?”.

There is still one big drawback in this viewpoint.

It’s caused by a simple misunderstanding, as Scala often becomes one’s first functional language, and introduced as a set of cool features mentioned above. Plus, Scala makes it possible to write an imperative code, so it is often used and considered as “Java with syntax sugar and a set of cool features”.

And that's wrong.

It is Java 8 that is still a conventional language with functional programming features.

Scala is a full-fledged functional programming language.

And in the next section, we'll try to explain why it makes all the difference, not only for your everyday code, but for the whole world of software development as well.

2. Scala is a Functional Programming Language

And it literally means “write less, do more”.

You are no more just another “construction worker”, with functional paradigm. You're more like an “architect”. We dare say that it's one of the major differences between conventional imperative and functional approaches for a developer.

Imperative programming implies that you write all steps you need in order to achieve the goal.

For example, logic of your application demands to select some strings that match your criteria from a big list of other strings. The best way to illustrate imperative approach to this task is to implement this logic in “Old Java” (7, 6 or older) style:

Java
```
       //We have some list of strings stringsToFilterList            

	//We know that we need to store filtered strings somewhere, so we make a storage for them;
        List<string> filteredList = new LinkedList<string>();
	//We know that to filter this list, we must iterate over all of its elements;
        for (stringToFilter : stringsToFilterList) {          
            if (StringUtils.isNotBlank(stringToFilter) && stringToFilter.startsWith("somePredicate")) { 
		/* The only meaningful operation in this code block, 
		we match our strings by set of conditions; */
               filteredList.add(stringToFilter); 
		/* And another technical-only operation inside of it, 
		   we know that after one of the iterated strings match our conditions, 
		   we must store it somewhere; */
            }
        }
```

So we have three technical steps and only one meaningful operation that has to do something with application logic.

In functional language like Scala, the same application logic implementation will be reduced to the meaningful operation only:

Java
```
        //We have some list of strings stringsToFilterList
        val filteredList = stringsToFilterList.filter(s => s.nonEmpty && s.startsWith("somePredicate"))
        //We still want to store filtered results somewhere, though.
```

Yup, only meaningful operation that has something to do with application logic left here.

You have just expressed what you really wanted to do, nothing more.

Still, there is an important thing to remember. Nothing will prevent you from literally mapping “Old Java” style code to Scala.

It means that if your mind works like “I want to filter all non-empty strings that start with something meaningful to the app logic”, your code will be nice and elegant, but if it works like “I need to create a storage for filtered data, iterate over initial data, check condition for every piece, store it if condition matches”, you will get code much like our “Old Java” example:

Java
```
        //Example of bad code. Nothing will prevent you from writing imperative code in Scala.
        val filteredList = ListBuffer[String]()
        for (stringToFilter <- stringsToFilterList) {
            if(stringToFilter.nonEmpty && stringToFilter.startsWith("somePredicate")) {
                filteredList += stringToFilter
            }
        }
```

Nothing will stop you, and you wouldn’t see why you need a functional programming language at all, it will look like “everything is just the same”. So with Scala (and functional programming in general), it's really “What you think is what you code”.

Functional programming offers many ways to express your algorithm in a way you see the most fit, just like mathematics.

Now, you may say “Wait! But Java 8 is doing things nicer!”, so let's take a look at that as well.

Java 8 has grasped lambda expressions and our example will look smaller:

Java
```
	List<string> filteredList = stringsToFilterList.stream().filter
	(s -> StringUtils.isNotBlank(s) && 
	s.startsWith("somePredicate")).collect(Collectors.toList());
       //Notice, even now there are still some technicalities here, like .stream() and .collect()
       //From our point of view, code even became less readable than it was.
```

It's still not the same. Oracle didn't change an approach at all. It's still our plain old Java with new crutches. They've just tried to cure the symptoms and bring in “these cool features everybody is talking about”, it didn't change an approach at all.

Even though common collection operations in Java 8 are now less literal, it doesn't change the fact that it still a conventional imperative programming language, and in many cases you still have to write lots, lots of code that makes sense only from technical point of view, but not from applications' logic.

Functional programming tries to avoid that. We've stated earlier, that functional programming provides many tools that enable succinct expression, just like math. And that's why it changes not only your everyday code, but software development process itself.

In fact, functional programming is very much about using mathematics to reason about programs. To do so, you need formalism that:

  1. Describes the programs themselves
  2. Describes how you can make proofs about properties they might have

And there are many models of computation that provide such formalisms, such as lambda calculus and turing machines (and there's certain degree of equivalency between them). Well, here's the interesting thing about math: the less powerful the formalism is, the easier it is to make proofs with it.

Or, to put it simpler, it's too hard to reason about imperative programs (especially with side-effects like mutability).

As a consequence, there's very little advance regarding concepts in imperative programming.

The famous Design Patterns were not arrived at through study, and they don't have any mathematical backing. Instead, they are the result of years and years of trial and error, and some of them have since proved to be misguided.

And who knows about the other dozens of "design patterns" seen everywhere?

Meanwhile, in functional programming community, things are much more lively.

For example, Haskell programmers came up with Functors, Monads, Co-monads, Zippers, Applicatives, Lenses - dozens of concepts with mathematical backing and, most importantly, actual patterns of how code is composed to make up programs. Things you can use to reason about your program, increase re-usability and improve implementation correctness regarding application logic.

Functional approach provides you with means to write better programs, not better ways to write some steps of your algorithms.

We'll cover some of these means, the ones that are distinct to Scala as a functional programming language, in the following sections.

3. Monads

A monad is a concept that has got to functional programming from math.

They're often called “programmable semicolons”, because semicolons are often used to chain together individual statements in many imperative programming languages (obviously, Java is no exception). Thus, “programmable” semicolon implies that between the statements, some extra code will be executed.

It is a structure that represents computations defined as sequences of steps: a type with a monad structure defines what it means to chain operations, or nest functions of that type together.

This allows the programmer to build pipelines that process data in steps, in which each action is decorated with additional processing rules provided by the monad.

By definition, monads are perfect for chaining stuff together, and Scala makes great use of it.

In this article, we won't cover either formal definition of a monad or what conditions must be met for something to be called a real monad.

We'll talk about benefits of monads in programming and how this concept was grasped and handled by Scala.

Among beginners, the most popular monads in Scala are:

1. “Maybe”M monad. “Maybe” monads are often referred to as “Optional Types”.

In Scala, maybe monad implemented as Option[T] class, which can be in two definite states: Some[T] (when some actual value of type T is stored inside) and None, when it stores nothing.

You can always wrap something in Option with Scala:

Java
```
 val maybeString = Option(someOtherString)
```

In Java, you would need to always check if there is an actual value inside by yourself, but in Scala, Option does this for you, and thanks to immutability, you don't need to check this again and again.

Scala provides quite handy helper methods for "maybe" monad:

Java
```
	//Direct matching of state
	maybeString match {
		case Some(s) => { //do something with this string }
		case None => { //do what you need to do in case there is nothing inside }
	}
            
	//Get value stored from monad, or use default one if there is nothing inside
	maybeString.getOrElse("defaultValueOfThisString")

	//Get value stored from monad, or execute a function if there is nothing inside
	//Very useful if some additional stuff must be made other from assigning a default value
	maybeString.orElse({
		//Here goes our function for when there is nothing inside
		log.warn("Using default value!!!") 
		//Not the best reason to have a function here, but hey.
		Some("defaultValueOfThisString")
	}).get //Thanks to orElse we'll never receive a None.get here

	//To quickly check if there is something inside without actually grabbing it
            if (maybeString.isEmpty) { ... }

	//To quickly check that there is something inside AND it meets some conditions without actually grabbing it
	if (maybeString.exists(_.startsWith("somePredicate"))) { ... }
```

So convenient! We bet you've already imagined the possibilities this brings to your code!

Maybe monad is pretty easy to implement, that's why it's pretty popular to adopt nowadays (check this Swift vs. Objective-C article), among other functional programming features, but for some reason Java 8 still hasn't incorporated it.

2. “Try”. Strictly speaking, this is not a monad, because it doesn't meet one of the formal conditions. Nevertheless, it behaves much like one and you can view it as a substitution for the usual try/catch routine.

Java
```
 Try({
     //...some code here
 })
 ...
 //You can call some other class, too
 Try(SomeClass.someMethod())
 ...
```

Ok, now we get to the good part. Try also has two distinct states: Success, when everything went fine, and Failure, for example when exception has occurred OR result doesn't meet some conditions of yours.

Let's take a look:

Java
```
 //direct matching of state
 Try(SomeClass.someMethod()) match {
     case Success(s) => { //do something with result }
     case Failure(e) => { //do something regarding an exception that was raised }
 }

 //When you want to do something for failure
 Try(SomeClass.someMethod()).recover({ //some fail-safe operations })

 //When there are additional conditions that must be met for Try to be a Success.
 Try(SomeClass.someMethod()).filter({ //some checks to mark try as Success })

 //When you just care about result and want to look at it as a "maybe" monad
 Try(SomeClass.someMethod()).toOption

 //Quick way to get value if everything went fine or a default value on failure, just like in option.
 Try(SomeClass.someMethod()).getOrElse("somethingDefault")

 //Or if you need to do something more than just return a default value
 Try(SomeClass.someMethod()).orElse({
     log.error("We couldn't get that thing for you, enjoy your default value")
     Success("somethingDefault")
 }).get

 //if you just want to know if everything went fine or not
 Try(SomeClass.someMethod()).isSuccess
 Try(SomeClass.someMethod()).isFailure

```

And, as usual with monads, you could combine all of the above as your needs dictate.

The usual try / catch routine pales compared to this. You can't be this flexible about your application workflow in Java, or other imperative languages, only functional approach allows this.

And yes, if you want a simple try / catch for some reason, it is still available to you.

3. “Future”. This is the most interesting one, as it opens the whole new world for beginner developer asynchronous programming.

Scala's concurrency model makes a good use of this monad, and it's what makes it so powerful and robust. You can now make bits of your code independent and non-blocking, and you still can block and wait for result. How cool is that?

Java
```
 val futureResponseBody: String = future {
     //we make an http request here and return response body as String
 }
 //This line of code will be executed without waiting for request to complete
 println("Yay! Wasn't blocked by request!")
```

Now, how we can handle it?

```
 //direct state matching
 futureResponseBody onComplete {
     case Success(responseBody) => { //do something with it }
     case Failure(e) => {  //do something regarding and exception that was raised }
 }

 //If you want, you can block and wait for the future with some timeout
 //(it will be resolved as Failure after that)
 futureResponseBody.map(body => //do something with response body)
 Await.result(futureResponseBody, 30 seconds)

 //do something with successfully resolved future
 futureResponseBody onSuccess { ... }

 //do something with failed future
 futureResponseBody onFailure { ... }

 //If you want to know if future is resolved without grabbing what's inside
 futureResponseBody.isCompleted

 //If you want to get instant value of the future as an Option
  futureResponseBody.value

```

As you see, monads in Scala comply to a shared set of rules and can easily be chained and converted to each other as per the convenience of the developer.

Monads in Scala enabled approaches that made traditionally tedious things fast and robust.

And again, this allows you to concentrate on implementing logic of your application and not to waste time on purely technical stuff.

4. Immutability

Immutability is what enables functional programming in first place, and as we try to illustrate, functional approach makes all the difference in software developers' experience.

Immutability is a paradigm by which no object can change its state after creation. If you need to change immutable objects state, you create a copy and change the required properties.

The easiest example of an immutable type for Java developer is String. Every Java developer knows that in Java, strings are immutable, and for every change a copy is created.

Immutability is a key to functional programming because it matches the goals of minimizing the parts that change, making it easier to reason about those parts.

So what's so good about that, anyway?

  1. No side effects. You don't have to worry that something will change an object you've passed somewhere. But why is that good? Keep reading.
  2. Execution speed up. With immutable objects, success or failure based on mutability is forever resolved once the object is constructed. That means less checks in runtime and more optimization from compiler, which gives faster execution of your code as a result.
  3. 100% Thread-safety. You don't have to worry about locking because shared objects can never change. You don't have to think through the code and figure out all the places where two threads might try to change the same object at the same time, and you don't have to write more code to prevent the problems this might cause.
  4. Cleaner code. The very fact of immutability of the object will make the code that makes use of it much, much cleaner and readable.

But what's so good about immutables from Java development's standpoint?

Immutables make a whole bunch of normally troublesome things in Java go away:

  1. By isolating the places where changes occur by severely restricting mutation, you create a much smaller space for errors to occur and have fewer places to test.
  2. Immutable object can never exist in unknown or undesirable state because of an exception. All initialization occurs at construction time, which is atomic in Java, so any exceptions occur before you have an object instance.
  3. As stated above, immutable objects are also automatically thread-safe and have no synchronization issues. And it's a huge benefit for Java concurrency model. If no object methods can modify its state, no matter how many of them and how often are being called parallel, they will work in their own memory space in stack.
  4. Because changes only occur upon construction, immutable classes make it trivial to write unit tests. You do not need a copy constructor, and you need never sweat the gory details of implementing a clone() method.
  5. It's trite to say but immutable objects make good candidates for use as keys in either Maps or Sets. Keys in dictionary collections in Java cannot change value while being used as a key, so immutable objects make great keys.

Ok, but what exactly does Scala handle better, to the point that you’d want to use it instead of Java?

Java's approach: Everything is mutable until specifically made otherwise, or in simple words: “You can make something immutable if you want, but how would you know that you need to?”.

In many cases, nobody bothers to do so. Yes, there are best practices and guidelines available, but developers have too much on their hands just dealing with everyday tasks and often have no time to explore and learn new features and best practices for programming language they use (despite the fact that it's exactly what they need to do the most).

Consider that:

1. You have to know how to make something immutable in Java and to have an interest in doing so, it's not as simple as one keyword:

  • All fields of the class must be final.
  • Class must have no mutating methods other than the constructor.
  • Class must be final so it cannot be overridden.
  • Which figures no default no-argument constructor for you.
  • Which figures you should have a constructor that inits all the fields.
  • Which figures you may need helper entities (maybe some DTOs), which should be immutable too.

From Java developer point of view, this already looks bothersome, especially when you realize that in result your class will be lone immutable in the sea of the mutables.

2. Minimization of production time is always a top priority in software development, and as you see, going with immutables in Java takes time and effort;

3. Enterprise software can always grow extensive due to ever lowering cost of machine time, so such an effort in existing project may cost more than additional server or two.

And you get that most of the senior developers don't bother with immutables, and junior developers don't care and just copycat code already present in project.

Scala's approach is different: Use immutable objects until you clearly need something to be mutable. While technically, you should still explicitly state mutability/immutability of an object, as a functional programming language Scala makes use of immutables by default wherever it's technically possible and tries to set programmers mind this way all the time, even with compiler warnings (“Hey, this thing could be immutable!”).

Okay, but how can this Scala approach will help an existing Java Project? The answer is: Easy.

Scala has full Java interoperability, so you can write a component, module, or just a single class in Scala and use it from your Java code, as well as you can call any Java class from Scala code.

So with Scala, you have all benefits of immutability without setbacks of its integration into conventional imperative programming language (where mutability is default), in your already existing project.

How cool is that?

5. Safe and Simple Concurrency Out Of the Box

Java 8 standard library provides only traditional thread-model concurrency.

By this model, the execution of the program is split up into concurrently running tasks.

This is similar to running multiple copies of one program in shared memory pool.

Problems with threads and traditional concurrency approach are well known:

  1. Thread creation is a pretty heavy operation by itself.
  2. Communication between threads is a headache.
  3. In general, it's difficult for a developer to get benefits from threading because of problems it brings, especially with mutable types. Concurrent modification, thread starvation, dead locks, you name it.

Traditional threading is opposed by much more simple and efficient actor model, which Scala standard library includes support for.

The actor model takes a different approach to concurrency, which should avoid the problems caused by threading and locking. As the name implies, this model defines each object as an actor, an entity that has a mailbox and a behavior.

This takes care of:

  1. Thread communication. Messages are easily exchanged between actors via their mailboxes.
  2. Thread control. For each message, a custom behavior can be defined. This includes things like sending a number of messages to other actors, create a number of actors and assume new behavior for the next message to be received;
  3. As the whole communication is done by messages, there is no shared state between actors, so there are no problems tied with that.

Scala also to some extent eliminates headache with locking thanks to its immutability approach.

Scala standard library includes support for the actor model out of the box. They are available through the scala.actors library.

Actors implementation is a great testament for the expressiveness of Scala: all functionality, operators and other language constructs included, is implemented in pure Scala, as a library, without requiring changes to Scala itself. Let's take a closer look.

Scala makes the distinction between thread-based and event-based actors.

Thread-based actors are each run in their own JVM thread. They are scheduled by the Java thread scheduler, which uses a preemptive priority-based scheduler.

When the actor enters a receive block, the thread is blocked until messages arrive. Thread-based actors make it possible to do long-running computations, or blocking I/O operations inside actors, without hindering the execution of other actors.

Event-based actors get rid of another traditional threading problem - huge overhead of threading which greatly limits amount of threads that can be created, thus, greatly reducing benefits from multi-threading in the first place.

So, how exactly are event-based actors different?

Event-based actors are not implemented by means of one thread per actor, yet instead they run on the same thread. An actor that waits for a message to be received is not represented by a blocked thread, but by a closure. This closure captures the state of the actor, such that its computation can be continued upon receiving a message. The execution of this closure happens on the thread of the sender.

This way event-based actors make a more light-weight alternative to thread-based actors, allowing for a large number of concurrently running actors (like, 100 000 of actors on a home-level machine). Event-based actors, though, bad for parallelism: since all actors execute on the same thread, there is no scheduling fairness.

To use event-based actor in Scala you should use a "react" block instead of a "receive" block in your actor.

There is also one big limitation to using event-based actors: upon entering a react block, the control flow can never return to the enclosing actor.

Scala allows programmers to mix thread-based actors and event-based actors in the same program, so you can use either scalable, lightweight event-based actors, or thread-based actors that allow for parallelism, depending on what you need.

To make something an actor in Scala, you simply extend Actor trait and implement receive or react block:

Java
```
    class SampleActor extends Actor with ActorLogging {
        def receive = {
            ...
        }
    }
```

Creating a new actor is also very simple:

Java
```
    //first we must init an actor system
    val sampleActorSystem = ActorSystem("SampleActorSystem")
    //now, create an actor
    val sampleActor = system.actorOf(Props[SampleActor], name = "sampleActor")
```

Sending a message is also a no-brainer:

Java
```
    //Structure of SampleMessage class
    //case class SampleMessage(msg: String)
    sampleActor ! SampleMessage("It's a test message")
```

And defining a behaviour for this message is also quite simple:

Java
```
    class SampleActor extends Actor with ActorLogging {
        def receive = {
            case SampleMessage(msg) = log.info(f"Got sample message: $msg")
        }
    }
```

And if for some reason, you want to use plain old Java concurrency, you can use that too.

Take this simplicity and flexibility, add benefits of using immutable types by default and you get that Scala's concurrency model is much, much superior to Java 8.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)