DEV Community

Gerbrand van Dieyen
Gerbrand van Dieyen

Posted on

Back and forth from Scala to Java

For over two years I've programmed nearly exclusively in Scala on the backend, while using mostly JavaScript and TypeScript on the frontend. Before that I mostly used Java for over 15 years, as well as a few other programming languages
Since a month I use Java, Java 8 even, on an assignment, and I thought I shared my experience.
The reason I use Java again, because the client uses Java and more broadly, I wondered how easy it would be to switch back if needed. After all I claim to be programming-language agnostic

First, going back wasn't as hard as I thought it was, it all came back to me pretty quickly. Of course I'd need to add ; and some more superfluous code, but that's doable.

Lambda's

I use Java 8, which is the first Java version to feature lambda's. I don't think I could bear a language without. I use lambda's heavily, probably more than people who'd never used a programming language.
Scala uses =>, Java uses ->, but that's ok.

Mutable data

Just as in Scala, mutable data-structures can lead to unpredictable behavior. In the Scala world people are by now pretty used to using immutable data-structures.
In the Java world, rather then thinking about how to transform data correctly, people still deliberate about anthropomorphic questions like what responsibilities an object has and how a Employee class relates to an employee in the real world.

Option

Java 8 introduced the Optional class, so there's no excuse for having NullPointerExceptions in your code (well, except when using older code and libraries).
Java 8 Optional has a map method. Annoyingly, it's orElse method of Option returns the actual value type instead of the value wrapped in an Optional, so you can't chain an orElse. I have no idea what the language designers were thinking. Well, as alternative I could use map, but that's a bit less readable:

      Optional<String>  firstName=Optional.of("John");
      Optional<String>  lastName=Optional.empty();
      Optional<String> name = firstName.map(Optional::of).orElse(lastName);

Fortunately, Java 9 introduced or.

Optional and streams

Also, Java's Optional can't be used as Iterable, while Scala's Option can. Allowing to treat Option as a collection of zero or one values helped me better understanding what an Option is and how you could use it beyond the isPresent() method.
Converting an Option to a Stream shouldn't be too hard:

    public static <T> Stream<T> stream(Optional<T> option) {
        return option.map(Stream::of).orElse(Stream.empty());
    }

Which will then allow me to write:

      Optional<String>  firstName=Optional.of("John");
      Optional<String> middleName=Optional.empty();
      Optional<String>  lastName=Optional.of("Doe");

      ...
   private String toName(Optional<String> firstName, Optional<String> middleName, Optional<String> lastName) {
      return Stream.of(
              stream(firstName),
              stream(middleName),
              stream(lastName))
              .flatMap(Function.identity())
              .collect(Collectors.joining(" "));
   }

That doesn't look so concise anymore, but arguably better than nested if else blocks.

Try catch finally and Either

In Java 8, when you use a resource that implements the Closeable interface working, a finally block to close the resource is no longer necessary. That's pretty nice:

    public String readMe() throws IOException {
        try(BufferedReader in = new BufferedReader(new FileReader("some file.txt"))) {
            return in.readLine();
        }
    }

At least when an exception is thrown, the resource is closed. What I greatly miss, is a Try class, Either or something similar so no exception is thrown by the method at all. There are a few libraries that do add something like Either, like functionalj, but of course these are non-standard so a bit harder to introduce at project.

Beyond the limits of imperative OO: annotations, aspects and reflection.

As is pretty common, Spring is also used to extend Java. Spring is based on the principle of aspect- or plugin-oriented programming and heavily relies on bytecode-enhancement and reflection to extend the language Java. In practice this means you use either annotations or config files.
The biggest disadvantage: everything is applied at runtime, e.g., when your application runs.
In Scala, the language itself is flexible enough using pattern matching, implicits, macros and other language constructs.
Scala pattern matching does use reflection in some cases, but most errors are catched compile time rather then runtime. And despite people complaining about implicit, having an error during compilation because you forgot to import an implicit is a lot easier to handle than an error that occurs when running your software because some configuration is wrong, or something fails during reflection.

Spring will give you pretty good errors when having a wrongly applied annotation, and IntelliJ does have good support for Spring. So most of the times you'll get reasonable descriptive warnings or errors for missing beans, wrong spring-configuration, etc, which makes Spring almost feel being part of the language. And it still feels cumbersome, if only because your code is cluttered with annotations.

Lombok, case classes

Scala introduced case classes, which allows you to write value based classes without a lot of boilerplate - or mistakes like accidentally committing a property in equals or hashCode.

Java by default doesn't. I'm very glad someone created Lombok and it seems well accepted.

import lombok.Value;

@Value
public class Employee {
    String firstName;
    String middleName;
    String lastName;
    long employNumber;
}

You can't do everything you could do in Scala, like pattern matching on a case class, and unlike Scala there's still a difference between methods and properties in Java.
But it's a whole lot better then generated equals, toString and get'ter methods.

IDE support for Lombok is very good, so you hardly notice you're using language extensions.

Conclusion

Switching back and forth from Scala to Java is doable, but I certainly wouldn't want to go back indefinitely. The Scala world is still thriving. Java is bearable, but Scala makes programming doable.

Top comments (6)

Collapse
 
mategreen profile image
matej

Hi,
I suggest you to use Vavr. I use it extensively for all Java projects.
I also prefer Immutables over Lombok, but it's just about the personal taste.

Collapse
 
josealonso profile image
José Ramón (JR) • Edited

Gerbrand @mategreen , thanks a lot for your comments!! I didn't know the vavr functional library. It's like Scala, amazing !!
Based on my investigations, I learned that only the Immutables and vavr libraries provide real immutable objects. I really like vavr, since it removes a lot of boilerplate code, it feels like Scala and doesn't use annotations. But I'm afraid it requires a big learning curve for many Java programmers. I met a Scala programmer who had to rewrite in Java an existing Scala project, because his company didn't find Scala developers. He told me he uses something similar to what vavr provides. I will tell him about that library.
My use case is refactoring a legacy code which is in Java 8 or 11. I didn't start the project yet, but I'm learning Kotlin in-depth, because I think it's easier to learn than Scala (or the vavr library) and it was designed with Java interoperability in mind.
Kotlin collections are read-only, not immutable. But there is a mature kotlinx.immutable library that is really easy to use. Kotlin can also be used in Spring, as you know.

To sum up, my plans for migrating a legacy Java code base to something more maintainable and functional consist of using Java and Kotlin at the same time. I think having immutable objects by default is crucial, like you have in Scala. Fortunately, you can have that in Kotlin using the above mentioned library.

You say you use vavr extensively for all Java projects, but you also use Lombok. I'm curious to know for what projects you use vavr. Are they only personal projects and if not, how do you achieve your team to get familiar with functional programming ?

Thanks for your insights.

Collapse
 
mategreen profile image
matej • Edited

sorry for my late answer.. yes, you're right! that's what exactly happened in our team. Due to re-org we merged with another team that started project in Scala (namely in Play framework) and we had to switch to Java as we didn't know anything about Scala (and functional programming ofc) that time. But I really liked it! To be honest, nowadays, it's much easier reason about vavr Futures than Java CompletionStage etc.
btw this book really helped me with FP:
Functional Programming In Java
Our stack is Play framework or Akka Http (now Pekko) and yes, I use Vavr for corp projects.

Collapse
 
josealonso profile image
José Ramón (JR)

I expected a bit longer article. From Scala or Kotlin I miss the higher order functions (in Java you have the built-in functional interfaces). And having to convert any collection to a stream before using map, filter or groupBy is cumbersome compared to Scala or Kotlin.
Finally, I wanted to know if the Immutables library is useful in Java projects. What is your experience, @mategreen ?

Collapse
 
mategreen profile image
matej

Hi @josealonso, if you would like to use scala like classes, e.g. Option, Either, Try or immutable collections, take a look at Vavr.
docs.vavr.io/
In case of collections, you get a lot of useful methods:
javadoc.io/doc/io.vavr/vavr/latest...

I used Immutables a lot, cause you get builders or factories for free, now we switched to Lombok for all new projects

Collapse
 
mategreen profile image
matej

btw what are you interested in specifically regarding this topic? I can share some snippets or write the post...