I’m a Clover developer at Atlassian, and I had an opportunity to work closely with new language features introduced in Java 8 during development of Clover 3.2.0 (which has the support for Java 8). I’d like to share my impressions about a major language feature – lambda functions. I’m pretty sure you’ve already read a lot of articles about lambdas, and already know how they are going to reduce boilerplate code. And I totally agree with this point. I can bet that most of anonymous in-line classes you have in your code might be changed to a simple lambda function, quite often written as a one-liner.

But are lambda functions perfectly designed? In my opinion – no. Let me show why.

Writing a lambda definition

Let’s start with writing a simple action listener:

[cc lang=’java’ line_numbers=’false’]
JButton button = new JButton(“Click me!”);
button.addActionListener(event -> button.setText(“Thank you!”));
[/cc]

This looks great. Short and concise as expected – A huge win for Java 8.

Writing a lambda declaration

So let’s start writing our own lambda caller! It will take a single BigInteger as input, make some computation on it, and return another BigInteger:

[cc lang=’java’ line_numbers=’false’]
public class Main {
public static void runCalc(WHAT_SHOULD_I_WRITE calc) {
// ???
}
public static void main(String[] args) {
runCalc(a -> a.multiply(a));
}
}
[/cc]

But how to declare a lambda signature in WHAT_SHOULD_I_WRITE? In Scala we could write:

[cc lang=’scala’ line_numbers=’false’]
def runCalc(calc: (BigInt => BigInt)) {
???
}
[/cc]

So, let’s try to do something similar in Java:

[cc lang=’java’ line_numbers=’false’]
public static void runCalc((BigInteger -> BigInteger) calc) {
// ???
}
[/cc]

Unfortunately, this code does not compile:

[cc lang=’text’ line_numbers=’false’]
java: illegal start of type
[/cc]

Do you think that maybe a syntax is different for such declaration? No. So what is wrong here? Actually nothing. It’s just impossible. Really.

There is no possibility to declare a signature of an anonymous function in Java 8.

What you have to do is to declare a functional interface, i.e. an interface having exactly one abstract method. And a signature of this method defines types of input arguments and a return type of a lambda function we can assign to it. In our case this is a single BigInteger argument and a BigInteger return value. So let’s write it:

[cc lang=’java’ line_numbers=’false’]
public class Main {
interface MyCalcLambda {
BigInteger run(BigInteger input);
}
public static void runCalc(MyCalcLambda calc) {
// ???
}
public static void main(String[] args) {
runCalc(a -> a.multiply(a));
}
}
[/cc]

In my opinion this is weird – why should I declare such interface at all? Why isn’t it possible to declare input types and a return type just in-line? Lambda functions have been introduced in Java 8 in order to get rid of anonymous in-line classes, right? So why, instead of this, do we have to declare functional interfaces?

There are two things which may cheer you up, however:

  • Writing lambda definition is more frequent than its declaration, so in 90 percent of cases you won’t have to write functional interfaces.
  • The java.function package contains few dozens of predefined functional interfaces, so in most cases you’ll just have to import them.

Calling a lambda

There is one piece missing in our code – calling the lambda function. What is natural in functional languages is that a function variable can be treated as a function. For instance, in Scala we can just call it:

[cc lang=’scala’ line_numbers=’false’]
def runCalc(calc: (BigInt => BigInt)) {
System.out.println(calc(10))
}
[/cc]

So, let’s call it in Java 8:

[cc lang=’java’ line_numbers=’false’]
public static void runCalc(MyCalcLambda calc) {
System.out.println(calc(BigInteger.TEN));
}
[/cc]

Oops. Another compilation error:

[cc lang=’text’ line_numbers=’false’]
java: cannot find symbol
symbol: method calc(java.math.BigInteger)
location: class Main
[/cc]

I’m sorry – it won’t work this way.

A variable holding a reference to a lambda function cannot be called as a function.

Unbelievable, isn’t it? Even good old C/C++ allowed this with function pointers.

What you have to do is to call a method from a functional interface. In our case it’s a run(BigInteger) method:

[cc lang=’java’ line_numbers=’false’]
public static void runCalc(MyCalcLambda calc) {
System.out.println(calc.run(BigInteger.TEN));
}
[/cc]

I really cannot understand why it’s not possible to use a typical function call.

Someone might argue that grammar could become more complex or even ambiguous. Yes, but shouldn’t this be a problem of a grammar and a javac compiler and not developers using Java?

Someone else might argue that Java symbolic name space allows to have a variable and a method with a same name, and in such case they could clash. Yes, but couldn’t a complier just produce a warning? Or have some visibility rules defined for this?

Summary

Introduction of lambda functions in Java 8 is a step in a right direction. But, compared to other JVM programming languages we have at hand, their design is not perfect. And I’m afraid it won’t be improved in a near future.

Read more from our developers at developer.atlassian.com.

What I miss in Java 8 lambda functions