Click here to Skip to main content
15,881,248 members
Articles / Programming Languages / Java

10 Differences Between Scala and Java 8: Part 2

Rate me:
Please Sign up or sign in to vote.
4.13/5 (4 votes)
17 Apr 2015CPOL9 min read 14.7K  
In a previous part of this article we were talking about major differences and similarities between Scala and Java. Here we continue our overview.

If you missed the first part of this article - welcome to read about first five differences here.

6. Traits in Scala vs. Virtual Extension Methods in Java. More behaviors!

Scala offers a great mechanism to extend your classes and flexibly enrich it with new behaviors.

The interesting part is that the class gains both the type plus all methods and state of the mixed-in traits (therefore traits are often called mix-ins, as they mix in new behavior and state into a class).

Technically, trait in Scala offers an interface, and optionally includes an implementation, and a class can "mix in" multiple traits.

Note that even though you can mix in any number of traits you want, Scala similarly to Java, has no multiple inheritance. In both Java and Scala a subclass can only extend a single superclass. But it's different with traits.

The good part is that Scala defines a clear set of precedence rules that determine when and what is executed within the multiple inheritance hierarchy, independent of the number of traits mixed in.

These rules provide us with all benefits of multiple inheritances without any of the problems associated with it.

So, what's so good about traits in Scala? They enable composition of classes from these traits, with traits being building blocks.

As always, let's see in an example. This is how a conventional logging routine is set up in Java:

”C++”
```
    class SomeClass {

        //First, to have logging for a class, you must initialize it
        final static Logger log = LoggerFactory.getLogger(this.getClass());

        ...
        //For logging to be efficient, you must always check, if logging level for current message is enabled                
        //BAD, you will waste execution time if log level is error, fatal, etc.
        log.debug("Some debug message");

        ...
        //GOOD, it saves execution time for something more useful
        if (log.isDebugEnabled()) { log.debug("Some debug message"); }
        //BUT looks clunky, and it's tiresome to write this construct every time you want to log something.

    }
```

It's very tiresome to always check for log level being enabled.

It would be good, if you could write this routine once and reuse it anywhere, in any class right?

Traits in Scala make it all possible:

”C++”
```
    trait Logging {

        lazy val log = LoggerFactory.getLogger(this.getClass.getName)
          
          //Let's start with info level...
          ...
          //Debug level here...
          def debug(msg: => Any) {
            if (log.isDebugEnabled) log.info(s"${msg}")
          }

          def debug(msg: => Any, throwable: => Throwable) {
            if (log.isDebugEnabled) log.info(s"${msg}", throwable)
          }
          ...
          //Repeat it for all log levels you want to use

    }

```

So now we got efficient logging routine in convenient style, as a reusable block. And to enable logging for any class, we just mix in our Logging trait!

”C++”
```
    //Now that's all it takes to add logging feature to your class
    class SomeClass extends Logging {
        ...
        //With logging trait, no need for declaring a logger manually for every class
        //And now, your logging rouitine is either efficient and doesn't litter the code!

        log.debug("Some debug message")
        ...
    }
```

With traits in Scala you are able to isolate generic functionality in a modular fashion. You can plug the isolated functionality into any class you need. Traits are reusable. Imagine the possibilities!

Now, Java 8 introduced Virtual Extension Methods (also called Default Methods), but motivation behind VEMs and traits is different.

In Scala, traits were always intended as modular building blocks, and Java has VEMs primarily enable API evolution and usage of VEMs to make building blocks is a pleasant, but limited to behavior side-effect.

So, what exactly is limited? Let's map our Logging trait to Java 8:

”C++”
```
    interface Logging {
        
        //See? Here's our problem. We can't get a reference to the implementing class.
        final static Logger log = LoggerFactory.getLogger(Loggable.class);

        //Let's start with info level...
        ...
        //Debug level here...
        void debug(String msg) default {
            if(log.isDebugEnabled()) log.debug(msg);
        }

        void debug(String msg, Throwable throwable) default {
            if(log.isDebugEnabled()) log.debug(msg, throwable);
        }
        ...
        //Repeat it for all log levels you want to use

    }
```

As you can see due to mechanism limitations we have to use the Loggable interface as logger, which would log all statements under Loggable instead of the implementing class, which greatly reduces logging control in your application. Not useful at all.

Ok, but what VEMs in Java 8 were intended for?

The main reason is backward compatibility. For example, for many existing interfaces it would be very beneficial to have support for lambda expressions in the form of higher-order functions.

You would expect java.util.Collection interface of Java 8 to provide a forEach(lambdaExpr) method, right?

If such a method was added to the interface without a default implementation, all implementing classes would have to provide one, and that's a straight road to compatibility hell.

But with VEMs a .forEach method, for example, could be added to java.util.Collection including a default implementation. Consequently, all implementing classes will automatically inherit this method and its implementation. No compatibility hell, everyone is happy. And if the implementing class is not satisfied with the default implementation, it can simply override it.

As you can see, VEMs in Java 8 are technical necessity and were not primarily intended as a construction material for developers’ use, while traits in Scala were introduced exactly for that, to make your software lighter and better.

That's why Scala's traits beat Virtual Extension Methods in Java 8.

7. Type enrichment. Enrich my library!

In the previous section, we've discussed traits in Scala and Virtual Extension Methods (or default methods) in Java 8. We've focused on class composition and enrichment of behaviors of your class with reusable blocks.

We've also covered the problem Virtual Extension Methods in Java 8 were supposed to handle:

Non-intrusive extension of class behavior, which is very important in terms of backwards compatibility, especially with language's standard library classes, such as collections.

What we didn't cover, however, is how Scala can handle such case. Thanks to implicit classes, you can define and bring new behaviors to any class with a single import.

Let's take a look:

”C++”
```
    //For example, we want to bring new functionality to Int class, because why not?
    object IntExtensions {
      implicit class IntPredicates(i: Int) {
        def isEven = i % 2 == 0
        def isOdd  = !isEven
      }
    }

    import IntExtensions._  // Bring implicit enrichment into scope.
    4.isEven  // Yay, we now have new functionality available!

```

One of the most noticeable usages of such an approach in standard libraries is bridging between Java and Scala collections by enriching standard collections with .asScala() and .asJava() methods, which is very handy when you call Scala class from Java code and vice versa.

You can enrich any class you want, with any functionality you want.

You simply can't be this flexible with Java 8, because it didn't grasp concept of implicit objects.

8. Support of really vital design patterns on language level, and no need in legacy ones!

You can see a number of articles comparing Java and Scala, which have sections like “A factory pattern in Scala”, “a Command pattern in Scala”.

Indeed, many Java developers are curious about how things are done in Scala, and often focus on implementation of design patterns, because it's something they use every day.

But as we stated earlier while talking about functional paradigm in general - these patterns are just a result of continuous trial and error in imperative programming, and many of them have already been proven wrong or even harmful, some of the must-have patterns of the past are considered anti-patterns now.

So do you really need conventional objective design patterns in Scala?

The answer is NO. Design patterns of imperative languages are often just a way to overcome technical limitations of programming language.

They have their disadvantages, and disadvantages are often disregarded because it's the only supportable way to get the job done.

With functional programming technical limitations of the language are much weaker and functional approach allows you to compose your code and application pretty much any way you want.

So you definitely shouldn't restrain yourself with something that came up as a result of old struggles.

For example, you don't need a Factory pattern in Scala, it is not as necessary as in Java.

With monads, higher-order functions, advanced pattern-matching, type inference and implicit objects of Scala there is virtually no situation when you have to explicitly find (and explicitly construct) handlers for data that differs technically, but is quite similar in terms of application business logic.

But what about simple and common design patterns, patterns that are dictated more by common sense than by technicalities? Something as basic as a Singleton?

Singleton is the most popular design pattern in object-oriented programming.

The main problem with Singleton and Java 8 is that you still must implement it yourself, every time.

And it's always an opening to mess things up.

You shouldn't have to do that, and Scala gets it very well. If you want a singleton in Scala - you already have it! It's incorporated into the language itself!

Just declare your class as an object:

”C++”
```
 object MySingleToneObject {
     //That's all, here is your singleton!
 }
```

And with this, we come to one of the important and distinctive features of Scala called “minimal codebase”. See you in next section!

9. Minimal codebase. No need for "IDE vomit".

It is well known feature of Scala, but still not adopted by Java. The most notorious example is declaring a class. How does it look in Java?

”C++”
```
  public class Person {
     private final Integer id;
     private String name;

     public Person(Integer id, String name) {
         this.id = id;
         this.name = name;
     }

     public Integer getId() {..}

     public String getName() {..}
     public void setName(String newName) {..}

     @Override
     public boolean equals(Object obj) {..}

     @Override
     public int hashCode() {..}
  }

```

That's why you don't fiddle with Java in interactive console, like you do with Scala.

Oh, you can say "Wait, but my IDE does this for me, it has generators for everything!".

But if IDE generates all these tons of code for you, and if this is an everyday routine, why should you, or your IDE do this at all?

Why not to make declaration shorter, why force monstrous IDEs to developer?

With this much code overhead, you can do practically nothing without boilerplates, IDEs and its code generators. It kills all the fun, you don't want to experiment and try something new.

By the time you just described everything (especially true for initial implementations) you're already tired and just want to get your task done and over with.

What prevented making it simpler in Java 8 like it is in Scala?

Here's how you declare the same class in Scala.

”C++”
```
 case class Person(@BeanProperty val id: Int, @BeanProperty var name: String)
 //@BeanProperty annotation makes compiler generate getters and setters java-style, like getId(), setName(..), etc. instead of default getter-setters.
```

That's all, compiler will do everything else. It's 21st century, declaring a class should not be a job, it's a trivial matter, and Scala gets it very well.

Initialization of a variable is also a trivial matter, but in Java it is still like this:

”C++”
```
 public final String myCoolString = "My cool final string";
```

Because Java's runtime-level type inference doesn't cover such a trivial matter.

In Scala everything you need to init a variable is to write:

”C++”
```
 val myCoolString = "My cool final string"
```

And talking about Strings, you still go with sh*tty formatters in Java:

”C++”
```
 log.debug(String.format("This is %s, %s = %d", SomeClass.someMethod(), someVariable, someStatusCode));
 //It looks especially bad when you need to pass a string elsewhere, like to logger.
```

While Scala offers powerful interpolation feature.

”C++”
```
 log.debug(s"This is ${SomeClass.someMethod}, $someVariable = $someStatusCode")
```

Looks cleaner and much more informative.

And if you really want to format a String, you can do it like this:

”C++”
```
 "This is %s, %s = %d".format(SomeClass.someMethod, someVariable, someStatusCode)
```

Also, unlike Java, Scala provides support for multi-line string literals, which is especially handy for storing something like SOL-statements:

C++
```
     """
         SELECT column1,
                column2
         FROM table1
         WHERE column1 > 0;
     """
```

It's not about "syntax sugar", it's about actual thought about development and implementation process. Even in Java 8 you still have to use workarounds to init a list, set or map upon declaration. Why?

Scala makes sure you won't have problems with trivial everyday stuff like this.

10. Code and runtime compatibility

It's a well-known feature of Scala, but still a very important one. Write any class of your old Java project in Scala, migrate it class by class as you go. And run it in your same old runtime without any problems.

Thanks to this, you won't miss any performance improvements from Java, as with JRE update, Scala receives all performance tweaks of newer Java releases, too!

Any Java library can be used in Scala code:

”C++”
    ```
     val httpPost = new HttpPost("http://targethost/login")
    val response = httpclient.execute(httpPost)
```

And vice-versa. Scala even has helpers to build bridges between and have code conformity:

  1. Annotations like @BeanProperty, so getting something from your Scala class won't look strange in Java code;
  2. Helper objects like JavaConversions and JavaConverters to bridge between Java and Scala collections, so you don't need to import and struggle with Java collections in your Scala code if you need it to be used from Java code;

JavaConversions provides a series of implicit methods that convert between a Java collection and the closest corresponding Scala collection, and vice versa.

JavaConverters uses type enrichment to “add” the .asScala method to the Java collections and the asJava method to the Scala collections, which return the appropriate wrappers discussed above.

And if you work with some third-party Scala library, you can call these converters from your Java code too.

Nice and elegant.

Conclusion

We hope that in this article we demonstrated that Scala is not just a syntax sugared set of features for Java, but is really much, much more. And even though Java 8 is trying to be more like Scala in some parts, Scala still represents another, better approach to software development through its functional paradigm.

Take a good look at Scala right now! (link to )

License

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


Written By
Marketing Redwerk
Ukraine Ukraine

Redwerk is a software development outsourcing company with two software development centers in Ukraine. Core technologies are Java, .NET, Python, iOS, Android, Scala.


Our core business is to establish and service fully managed dedicated software development teams in Ukraine, as well as deliver custom software development projects for our customers world-wide.
Company website

This is a Organisation (No members)


Comments and Discussions

 
-- There are no messages in this forum --