Java 8 vs Scala — Part I λE

Hussachai Puripunpinyo
Zappos Engineering
Published in
9 min readNov 5, 2015

Finally, after several years of waiting, Java 8 comes with higher-order functions I love Java but I have to admit that Java syntax is quite verbose compared to other modern languages. Now, with lambda expressions, I can write code that is shorter while still readable (sometimes, even more readable than the traditional way).

Java 8 was released on March 3, 2014 but I just recently had a chance to play with it. Since I know Scala, I wanted to compare Java 8 and Scala in terms of expressiveness and performance. The subject of this comparison is Stream API and I’m going to show you how to manipulate collections using Stream API.

I will split this article into 3 parts since it will be very long if I put them altogether.

Part 1. Lambda expressions

Part 2. Stream API vs Scala collection API

Part 3. Trust no one, bench everything. (I took this phrase from sbt-jmh)

First, let’s take a look at lambda expressions in Java 8. I don’t know why they call this feature lambda expressions when the expression part is substitutable. I can use statements instead of an expression and I can say.. ah ha Java 8 also supports lambda statement(s). Yeah we are talking about the same thing where the language treats functions as a first-class citizen. The function can be passed around either through argument or return value because functions become objects. Java is a statically and strongly typed language. So, functions must have a type, in this case it’s an interface. On the other hand, lambda functions are objects that implement the functional interface. You don’t have to worry about creating this function object, the compiler will figure it out for you. Unfortunately, Java doesn’t have superb type inference like Scala. If you want to declare a lambda expression, you have to specify the target type. It’s actually acceptable and understandable since Java has to maintain backward compatibility and it’s doing this job very well. For example, Thread.stop() was released in JDK version 1.0 and it has been marked as deprecated for more than a decade, but it’s still there today. So, you cannot expect Java to change the syntax radically just because language xyz has better syntax (or approach).

So, the Java 8 language designer(s) came up with a cool idea. It is a functional interface! Functional interface is an interface that has only one abstract method. You know that most existing callback interfaces already satisfy this requirement. So, we can reuse those interfaces without modifying them. @FunctionalInterface is an annotation indicating that the annotated interface is functional interface. This annotation is optional and it doesn’t do anything special to interface except a requirement check.

Remember that lambda expression must have type and that type must have only one abstract method.

//Before Java 8
Runnable r = new Runnable(){
public void run(){
System.out.println(“This should be run in another thread”);
}
};
//Java 8
Runnable r = () -> System.out.println(“This should be run in another thread”);

What about a function that takes an argument (or arguments) and returns something?

To help with this, Java 8 comes with a set of common functional interfaces that you may use. They are in java.util.function package.

Function<String, Integer> parseInt = (String s) -> Integer.parseInt(s);

The argument type can infer from Function like a diamond operator in Java 7. So, we can omit it as well as parenthesis. We can rewrite above function as the following:

Function<String, Integer> parseInt = s -> Integer.parseInt(s);

What about a function that takes 2 arguments?

No worry Java 8 has BiFunction for that.

BiFunction<Integer, Integer, Integer> multiplier = (i1, i2) -> i1 * i2; //you can’t omit parenthesis here!

Can you guess the interface name of the function that takes 3 arguments?

TriFunction? Sorry the language designer decided to stop at BiFunction. Otherwise, we would have TriFunction, QuadFunction, PentFunction, and so on. In case you don’t know the meaning of these numbers, I took it from IUPAC rules. However, we can define our own TriFunction as the following

@FunctionalInterface
interface TriFunction<A, B, C, R> {
public R apply(A a, B b, C c);
}

Then import that interface and use it as a type of our lambda expression

TriFunction<Integer, Integer, Integer, Integer> sumOfThree = (i1, i2, i3) -> i1 + i2 + i3;

I think you already know why language designer stops at BiFunction.

If you still don’t know why, let’s take a look at PentFunction. Assume that we have already defined PentFunction interface somewhere.

PentFunction<Integer, Integer, Integer, Integer, Integer, Integer> sumOfFive = (i1, i2, i3, i4, i5) -> i1 + i2 + i3 + i4 + i5;

Can you imagine how long EnnFunction is? (enn means 9) You must have to declare 10 types (first 9 for arguments and the last one for return type) and the whole line will contain only types. You may be wondering, do we really have to declare a type?

The answer is “yes we do :(”. That’s why I said Scala type inference is more superior than Java.

Scala also has a type for its lambda expression. In Scala, you can have a lambda expression that can take up to 22 arguments it means that Scala also has a type for each function (Function0, Function1, … Function22). Function type in Scala is a trait. Trait is close to abstract class in Java but it can be used as a mixin.

If you want more than 22 arguments, I’d say that there is something wrong in your design. You may want to consider having type for a set of arguments you are passing.

I’m not gonna go into detail about lambda expressions as there is a lot of information out there. You can read some here and here.

Let’s take a look at Scala counterpart. Scala is also a statically and strongly typed language like Java but it is a functional language from the start. So, it blends object-oriented and functional programming paradigms together quite well.

I cannot show you a Runnable example (like I did above for Java) in Scala because it takes a different approach from Java. Scala has its own way of solving problems. So, I will show you the Scala approach instead.

Future(println{“This should be run in another thread”})

is equivalent to the following Java 8 code

//assume that you have instantiated ExecutorService beforehand.
Runnable r = () -> System.out.println(“This should be run in another thread”);
executorService.submit(r);

If you want to declare a lambda expression, you can do so without having to declare an explicit type like in Java.

//Java 8
Function<String, Integer> parseInt = s -> Integer.parseInt(s);
//Scala
val parseInt = (s: String) => s.toInt
//or
val parseInt:String => Int = s => s.toInt
//or
val parseInt:Function1[String, Int] = s => s.toInt

Wow! you have so many ways to declare type in Scala. Let compiler do its job.

What about PentFunction?

//Java 8
PentFunction<Integer, Integer, Integer, Integer, Integer, Integer> sumOfFive = (i1, i2, i3, i4, i5) -> i1 + i2 + i3 + i4 + i5;
//Scala
val sumOfFive = (i1: Int, i2: Int, i3: Int, i4: Int, i5: Int) => i1 + i2 + i3 + i4 + i5;

Scala is shorter because you don’t have to declare interface type and the Integer type in Scala is Int (richer version of Integer with a shorter name). Shorter doesn’t always mean better. Scala’s approach is better not because it is shorter but because it is more readable. The context of type is right there in the argument list. You can figure out the type of an argument at a glance.

If you don’t agree, take a look at this again.

//Java 8
PentFunction<String, Integer, Double, Boolean, String, String> sumOfFive = (i1, i2, i3, i4, i5) -> i1 + i2 + i3 + i4 + i5;
//Scala
val sumOfFive = (i1: String, i2: Int, i3: Double, i4: Boolean, i5: String) => i1 + i2 + i3 + i4 + i5;

In Scala, you can tell almost instantly that the type of i3 is Double but in Java 8, you have to count it to find out what type of i3 is except you have superman eyes. You may also argue that Java can do the same. Yes you’re right but it will look like this:

PentFunction<Integer, String, Integer, Double, Boolean, String> sumOfFive = (Integer i1, String i2, Integer i3, Double i4, Boolean i5) -> i1 + i2 + i3 + i4 + i5;

It’s just plain terrible, isn’t it?. Cause you have to repeat types over and over and over…and over.

In addition, Java 8 doesn’t have PentFunction for you. You have to define it yourself.

@FunctionalInterface
interface PentFunction<A, B, C, D, E, R> {
public R apply(A a, B b, C c, D d, E e);
}

Scala is better then? I didn’t say that but in some ways, yes. There are a lot of things that Scala is worse than Java. I’m not going to tell you which one is better based on my opinion. I’m comparing Java 8 to Scala because Scala is a functional language and Java 8 supports some functional features. So I had to find a functional language to compare these features.

Scala was a great candidate because it is a functional language that runs on JVM. You may see that Scala has a better syntax and approach when it comes to functional, simply because Scala is a functional language and the language designer of Java has much more constraints to design something new without breaking something old.

So even though there are some limitations to what Java can do syntactically with lambda expression, there are some cool features being introduced with Java 8. For example, the method reference feature which is a shorthand way of writing lambda expressions by reusing existing methods. Wait a minute. Making lambda shorter… What???

Function<String, Integer> parseInt = s -> Integer.parseInt(s);

can be rewritten using method reference as the following

Function<String, Integer> parseInt = Integer::parseInt;

You can use a method reference with instance method as well. I will point out why using method reference with instance method is pretty useful when we talk about the Stream API in Part 2.

Method reference construction rules

1. (args) -> ClassName.staticMethod(args);
can be rewritten as ClassName::staticMethod;

Function<Integer, String> intToStr = String::valueOf;

2. (instance, args) -> instance.instanceMethod(args);
can be rewritten as ClassName::instanceMethod;

BiFunction<String, String, Integer> indexOf = String::indexOf;

3. (args) -> expression.instanceMethod(args);
can be rewritten as expression::instanceMethod;

Function<String, Integer> indexOf = new String()::indexOf;

Did you notice something odd about rule number 2? It’s a bit confusing right? Even though indexOf function takes only 1 argument, the target type is BiFunction which implies it should take 2 arguments. Actually, this oddity is used a lot in Stream API and it makes sense there when you don’t see the type name.

pets.stream().map(Pet::getName).collect(toList());
// The signature of map() function can be derived as
// <String> Stream<String> map(Function<? super Pet, ? extends String> mapper)

From rule number 3, you may be wondering, can we put lambda expression instead of new String()?
You can construct an object using method reference like this

Supplier<String> str = String::new;

So, can we do this?

Function<Supplier<String>, Integer> indexOf = (String::new)::indexOf;

No, we cannot. It doesn’t compile and the compiler complains that “The target type of this expression must be a functional interface”. The error message is quite misleading and it seems like Java 8 does not support type inference through generic parameter. Even when you use an existing ‘instance’ of a FunctionalInterface (like ‘str’ above), you would see another error “The type Supplier<String> does not define indexOf(Supplier<String>) that is applicable here”. The functional interface of String::new is Supplier<String> and it has only method named get(). indexOf is an instance method belonging to the String object. Thus, we have to rewrite the code above as the following.

Function<String, Integer> indexOf = ((Supplier<String>)String::new).get()::indexOf;

Does Java 8 support currying (partial function)?
It does, but you cannot use a method reference here.
You can think of a partial function as the function that returns a function instead of a result.

I’m going to show you the trivial example using currying. I’m sorry if the example doesn’t make much sense to use a currying. We usually do something with the parameter(s) before passing to the function. Anyway, let’s take a look at how we implement a partial function using lambda expression.

Suppose that you want to implement the function that adds two integers using currying.

IntFunction<IntUnaryOperator> add = a -> b -> a + b;
add.apply(2).applyAsInt(3); //the result is 4! I'm kidding it's 5.

The function can take two parameters at the same time

Supplier<BiFunction<Integer, Integer, Integer>> add 
= () -> (a, b) -> a + b;
add.get().apply(2, 3);

Now let’s take a look at Scala approach.

val add = (a: Int) => (b: Int) => a + b
add(1)(2)
val add = () => (a: Int, b: Int) => a + b
add2()(1,2)

Scala approach is shorter than Java because of the type inference and the syntactic sugar. In Scala, you don’t have to call apply method explicitly on a Function trait. The compiler will translate () to apply method behind the scene.

If you find any errors, please let me know by commenting below. Thank you :)

--

--

Hussachai Puripunpinyo
Zappos Engineering

Daytime stay-at-home dad, nighttime entrepreneur. A man with dreams far greater than himself. My work: https://tailrec.io