Java 8 lambda expressions help eliminate boilerplate code that makes the syntax verbose and less clear. For instance, consider the standard basic click listener:
myButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d("debug", "Button clicked");
}
});
Lambda expressions can greatly simplify this code, especially in this case where the event listener has only one method that needs to be implemented:
myButton.setOnClickListener(v -> Log.d("debug", "Button clicked"); );
If you have more than one line to execute, then you should surround the block of code with braces:
myButton.setOnClickListener(v -> {
Log.d("debug", "Button clicked");
Toast.makeText(MyActivity.this, "here", Toast.LENGTH_LONG).show();
});
You may also notice in some cases lambda expressions also contain double colons ::
. These refer to a new syntax in Java 8 known as method references. You can reference a class or instance and pass along the method that will handle the event:
public void onCreate() {
myButton.setOnClickListener(this::logError);
}
public void logError(View v) {
Log.d("debug", "Button clicked");
}
Lambda expressions are especially helpful in RxJava as well. Take a look at the code below for creating an Observable and subscribing an Observer to it.
Creating and subscribing to an observable without lambdas:
Observable.just("1", "2", "3")
.subscribe(new Subscriber<String>() {
@Override
public void onCompleted() {
Log.d("debug", "complete");
}
@Override
public void onError(Throwable throwable) {
Log.d("debug", throwable.getMessage());
}
@Override
public void onNext(String s) {
Log.d("debug", s);
}
});
Consider the same code with lambda expressions:
Observable.just("1", "2", "3")
.subscribe(
value -> Log.d("debug", value),
throwable -> Log.d("debug", throwable.getMessage()),
() -> Log.d("debug", "complete"));
Lambda expressions rely on type inference to fill in the blanks. Notice that the right-hand side of the arrow does not require a return statement if you do not surround the block with {
and }
. Also notice that a function with zero or multiple arguments need parenthesis enclosing them.
You can look to the left of Android Studio to see how it is inferring which type to use:
Make sure you have JDK 8 installed or higher. Click here in case you need to download it. If you are using a continuous integration service, you should also make sure to be specifying a JDK 8 environment.
To use Java 8 lambda expressions in your Android code, you can use the Gradle Retrolambda plugin developed by Evan Tatarka. While Android Jack toolchain is also an option, it is currently deprecated as per this announcement.
build.gradle
file and add the following dependency:buildscript {
dependencies {
classpath 'me.tatarka:gradle-retrolambda:3.5.0'
}
}
build.gradle
file to apply the me.tatarka.retrolambda plugin:
apply plugin: 'com.android.application'
apply plugin: 'me.tatarka.retrolambda' // make sure to apply last!
Make sure to specify the plug-in last, especially if the android-apt
plug-in used according to this article.
compileOptions
block, then sourceCompatibility
and targetCompatibility
Java version should be set as 1.8android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
retrolambda
block:retrolambda {
jdk '/path/to/java-8/jdk'
}
java.lang.UnsupportedOperationException: Unknown ASTNode child: LambdaExpression
. To get around this issue, you need to add this third-party package that replaces the parsing engine for Java to support lamda expressions:buildscript {
dependencies {
classpath 'me.tatarka.retrolambda.projectlombok:lombok.ast:0.2.3.a2'
}
}
-dontwarn java.lang.invoke.*
Android provided a way to use some Java 8 language features including lambda expressions
in your Android project by enabling the Jack toolchain. You should not use this approach currently since it is deprecated. To do this, edit your module level build.gradle
file as follows:
android {
...
defaultConfig {
...
jackOptions {
enabled true
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
Sync your gradle file, if you encounter any build error, you may need to download the latest Android SDK Build-tools
from the SDK Manager
.
Known issues with using the Jack toolchain
Android Studio Instant Run does not currently work with Jack and will be disabled while using the new toolchain.
Because Jack does not generate intermediate class files when compiling an app, tools that depend on these files for example, lint detectors, do not currently work with Jack.
Annotation support for libraries such as Dagger 2 to show you in your Android project may not work with Jack. The most recent experimental releases are starting to add this support.
If you wish to convert your code to lambda expressions, move your cursor to the slightly greyed out section of your anonymous class and look on the left-hand side of the Android Studio editor for the light bulb:
Once you see the Replace with lambda
appear, you can also apply Fix all
click on the left-hand side to convert all the possible candidates automatically as outlined here.
An exception has occurred in the compiler (1.8.0_05)
or com.sun.tools.javac.code.Symbol$CompletionFailure
or java.lang.invoke.MethodType not found
?
apply plugin: 'retrolambda'
and apply plugin: "android"
. build.gradle
to ensure correctness and consistency. This guide was originally drafted by Adegeye Mayowa.