One of the new additions the Android N brings is support for several Java 8 language features:
– Lambda expressions
– Default and static interface methods, and
– Repeatable annotations
In this blog post we will take a look at the lambda expressions.
Setting up development environment
In order to start using these features however, the development environment needs some adjustments. The support of Java 8 language features requires a new compiler called Jack, which currently is supported only in Android Studio 2.1. So if you want to use Java 8 language features, you need to use Android Studio 2.1.
Enabling support for Java 8
To enable support for Java 8 in your Android project, the build.gradle file has to be adjusted by setting the compileOptions to version 1.8 and jackOptions enabled.
android { ... defaultConfig { ... jackOptions { enabled true } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } }
Lambda Expressions
Lambda expressions are a new feature included in the Java 1.8. A lambda expression basically is a block of code that can be passed around to be executed later. This, in fact, is very similar to anonymous classes. However, unlike anonymous classes, lambda expressions are succinct, as a result producing less verbose code.
Consider the following 3 examples:
button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Log.d(TAG, "Clicked."); } }); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Log.d(TAG, "Position: " + position); Log.d(TAG, "Id: " + id); } }); Runnable runnable = new Runnable() { @Override public void run() { Log.d(TAG, "From Runnable"); } }; new Thread(runnable).start();
Converting the above pieces of code to use lambda expressions will look like this:
button.setOnClickListener(view -> Log.d(TAG, "Clicked.")); listView.setOnItemClickListener((parent, view, position, id) -> { Log.d(TAG, "Position: " + position); Log.d(TAG, "Id: " + id); } Runnable runnable = () -> Log.d(TAG, "From Runnable"); newThread(runnable).start();
It is clear that the code with the lambdas is shorter. Specifically, this comes from:
– removal of class instantiation, eg.: new View.OnClickListener(),
– removal of method name, return type, and access modifier, eg.: public void onClick(View view)
– as well as removal of parameter types, eg.: only view, instead of View view.
And what is left, are the actual parameters and the method body.
Lambda Expression syntax
The basic format of a lambda expression is: a list of comma separated parameters, the “->” symbol, and the body.
param1, param2, paramN -> { // body }
1. Parameter types are optional, but they can be also specified in the expression:
// without parameter types (parent, view, position, id) -> { Log.d(TAG, "Position: " + position); Log.d(TAG, "Id: " + title); } // with parameter types (AdapterView<?> parent, View view, int position, long id) -> { Log.d(TAG, "Position: " + position); Log.d(TAG, "Id: " + title); }
2. If the body of the method has only one line, then the curly brackets can be ommited:
// with curly brackets (parent, view, position, id) -> { processItemClick(position); } // without curly brackets (parent, view, position, id) -> processItemClick(position)
3. If the expression has only one parameter, then the parenthesis around parameter can be omitted:
// with parenthesis (view) -> Log.d(TAG, "Clicked.") // without parenthesis view -> Log.d(TAG, "Clicked.")
4. The return keyword is optional if the body has a single expression to return the value;
// example interface public interface Calculator { int calculate(int a, int b); } // with return Calculator calculator = (a, b) -> {return a + b;}; // without return Calculator calculator = (a, b) -> a + b;
5. Lambda expression that does not take any arguments:
() -> Log.d(TAG, "From Runnable")
Functional interfaces
Lambdas are treated as instances of a special interface type called functional interface. A functional interface in fact is nothing but an interface with a single abstract method. We already saw several examples of such interfaces; Runnable, Callable, OnClickListener, OnItemClickListener are all examples of functional interfaces.
A new annotation called @FunctionalInterface was introduced to mark an interface as such.
@FunctionalInterface public interface Calculator { int calculate(int a, int b); }
The annotation is optional, but it is highly recommended; it clearly communicates the intent of the interface and it also allows the compiler to perform additional checks.
@FunctionalInterface public interface Calculator { int calculate(int a, int b); // *** this will break compilation *** void print(String result); } @FunctionalInterface public interface Calculator { int calculate(int a, int b); // this will NOT break the compilation, // because print is a default method default void print(String result){ Log.d(TAG, result); } }
Lambda expressions might led you to the wrong idea that they are something that is going to replace anonymous classes, and now seeing that a lambda expression can be converted from an interface with only one abstract method, you might wonder how useful is this. If this is so, then I would like to recall that a lambda expression is a mechanism that allows you to pack functionality and pass it around, in a succinct way. So no, lambdas, are not intended to replace anonymous classes.