Get hands-on training for JIRA Software, Confluence, and more at Atlassian Summit Europe. Register now ›

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.

Fresh ideas, announcements, and inspiration for your team, delivered weekly.

Subscribe now

Fresh ideas, announcements, and inspiration for your team, delivered weekly.

Subscribe now