GeezerVisonThe World Wide Web, restored to 800 pixels of glory. | ||||||
| Home|About|Gaming cabinet|Archives|How it works|Starter sites | ||||||
GeezerVison is showing jenkov.com — Java Lambda Expressions Java Language
Java Lambda Expressions
Java lambda expressions are new in Java 8. Java lambda expressions are Java's first step into functional programming. A Java lambda expression is thus a function which can be created without belonging to any class. A Java lambda expression can be passed around as if it was an object and executed on demand. Java lambda expressions are commonly used to implement simple event listeners / callbacks, or in functional programming with the Java Streams API. Java Lambda Expressions are also often used in functional programming in Java . If you prefer video, I have a video version of this tutorial in this Java Lambda Expression YouTube Playlist. Here is the first video in this playlist:
Java Lambdas and the Single Method InterfaceFunctional programming is very often used to implement event listeners. Event listeners in Java are often defined as Java interfaces with a single method. Here is a fictive single method interface example: public interface StateChangeListener {
public void onStateChange(State oldState, State newState);
}
This Java interface defines a single method which is called whenever the state changes (in whatever is being observed).
In Java 7 you would have to implement this interface in order to listen for state changes. Imagine you have a class
called public class StateOwner {
public void addStateListener(StateChangeListener listener) { ... }
}
In Java 7 you could add an event listener using an anonymous interface implementation, like this: StateOwner stateOwner = new StateOwner();
stateOwner.addStateListener(new StateChangeListener() {
public void onStateChange(State oldState, State newState) {
// do something with the old and new state.
}
});
First a In Java 8 you can add an event listener using a Java lambda expression, like this: StateOwner stateOwner = new StateOwner();
stateOwner.addStateListener(
(oldState, newState) -> System.out.println("State changed")
);
The lambda expressions is this part: (oldState, newState) -> System.out.println("State changed")
The lambda expression is matched against the parameter type of the
Java lambda expressions can only be used where the type they are matched against
is a single method interface.
In the example above, a lambda expression is used as parameter where the parameter type was the
Matching Lambdas to InterfacesA single method interface is also sometimes referred to as a functional interface. Matching a Java lambda expression against a functional interface is divided into these steps:
If the answer is yes to these three questions, then the given lambda expression is matched successfully against the interface. Interfaces With Default and Static MethodsFrom Java 8 a Java interface can contain both default methods and static methods. Both default methods and static methods have an implementation defined directly in the interface declaration. This means, that a Java lambda expression can implement interfaces with more than one method - as long as the interface only has a single unimplemented (AKA abstract) method. In other words, an interface is still a functional interface even if it contains default and static methods, as long as the interface only contains a single unimplemented (abstract) method. Here is a video version of this little section:
The following interface can be implemented with a lambda expression: import java.io.IOException;
import java.io.OutputStream;
public interface MyInterface {
void printIt(String text);
default public void printUtf8To(String text, OutputStream outputStream){
try {
outputStream.write(text.getBytes("UTF-8"));
} catch (IOException e) {
throw new RuntimeException("Error writing String as UTF-8 to OutputStream", e);
}
}
static void printItToSystemOut(String text){
System.out.println(text);
}
}
Even though this interface contains 3 methods it can be implemented by a lambda expression, because only one of the methods is unimplemented. Here is how the implementation looks: MyInterface myInterface = (String text) -> {
System.out.print(text);
};
Lambda Expressions vs. Anonymous Interface ImplementationsEven though lambda expressions are close to anonymous interface implementations, there are a few differences that are worth noting. The major difference is, that an anonymous interface implementation can have state (member variables) whereas a lambda expression cannot. Look at this interface: public interface MyEventConsumer {
public void consume(Object event);
}
This interface can be implemented using an anonymous interface implementation, like this: MyEventConsumer consumer = new MyEventConsumer() {
public void consume(Object event){
System.out.println(event.toString() + " consumed");
}
};
This anonymous MyEventConsumer myEventConsumer = new MyEventConsumer() {
private int eventCount = 0;
public void consume(Object event) {
System.out.println(event.toString() + " consumed " + this.eventCount++ + " times.");
}
};
Notice how the anonymous A lambda expression cannot have such fields. A lambda expression is thus said to be stateless. Lambda Type InferenceBefore Java 8 you would have to specify what interface to implement, when making anonymous interface implementations. Here is the anonymous interface implementation example from the beginning of this text: stateOwner.addStateListener(new StateChangeListener() {
public void onStateChange(State oldState, State newState) {
// do something with the old and new state.
}
});
With lambda expressions the type can often be inferred from the surrounding code. For instance,
the interface type of the parameter can be inferred from the method declaration of the stateOwner.addStateListener(
(oldState, newState) -> System.out.println("State changed")
);
In the lambda expression the parameter types can often be inferred too. In the example above, the compiler can infer their type from the
Lambda Parameters
Since Java lambda expressions are effectively just methods, lambda expressions can take parameters just like methods.
The public void onStateChange(State oldState, State newState); As a minimum the number of parameters in the lambda expression and the method must match. Second, if you have specified any parameter types in the lambda expression, these types must match too. I haven't shown you how to put types on lambda expression parameters yet (it is shown later in this text), but in many cases you don't need them. Zero ParametersIf the method you are matching your lambda expression against takes no parameters, then you can write your lambda expression like this: () -> System.out.println("Zero parameter lambda");
Notice how the parentheses have no content in between. That is to signal that the lambda takes no parameters. One ParameterIf the method you are matching your Java lambda expression against takes one parameter, you can write the lambda expression like this: (param) -> System.out.println("One parameter: " + param);
Notice the parameter is listed inside the parentheses. When a lambda expression takes a single parameter, you can also omit the parentheses, like this: param -> System.out.println("One parameter: " + param);
Multiple ParametersIf the method you match your Java lambda expression against takes multiple parameters, the parameters need to be listed inside parentheses. Here is how that looks in Java code: (p1, p2) -> System.out.println("Multiple parameters: " + p1 + ", " + p2);
Only when the method takes a single parameter can the parentheses be omitted. Parameter TypesSpecifying parameter types for a lambda expression may sometimes be necessary if the compiler cannot infer the parameter types from the functional interface method the lambda is matching. Don't worry, the compiler will tell you when that is the case. Here is a Java lambda parameter type example: (Car car) -> System.out.println("The car is: " + car.getName());
As you can see, the type ( var Parameter Types from Java 11
From Java 11 you can use the Function<String, String> toLowerCase = (var input) -> input.toLowerCase();
The type of the parameter declared with the Lambda Function Body
The body of a lambda expression, and thus the body of the function / method it represents, is specified
to the right of the (oldState, newState) -> System.out.println("State changed")
If your lambda expression needs to consist of multiple lines, you can enclose the lambda function body inside
the (oldState, newState) -> {
System.out.println("Old state: " + oldState);
System.out.println("New state: " + newState);
}
Returning a Value From a Lambda ExpressionYou can return values from Java lambda expressions, just like you can from a method. You just add a return statement to the lambda function body, like this: (param) -> {
System.out.println("param: " + param);
return "return value";
}
In case all your lambda expression is doing is to calculate a return value and return it, you can specify the return value in a shorter way. Instead of this: (a1, a2) -> { return a1 > a2; }
You can write: (a1, a2) -> a1 > a2;
The compiler then figures out that the expression Lambdas as ObjectsA Java lambda expression is essentially an object. You can assign a lambda expression to a variable and pass it around, like you do with any other object. Here is an example: public interface MyComparator {
public boolean compare(int a1, int a2);
}
MyComparator myComparator = (a1, a2) -> a1 > a2; boolean result = myComparator.compare(2, 5); The first code block shows the interface which the lambda expression implements. The second code block shows the definition of the lambda expression, how the lambda expression is assigned to variable, and finally how the lambda expression is invoked by invoking the interface method it implements. Variable CaptureA Java lambda expression is capable of accessing variables declared outside the lambda function body under certain circumstances. I have a video version of this section here:
Java lambdas can capture the following types of variables:
Each of these variable captures will described in the following sections. Local Variable CaptureA Java lambda can capture the value of a local variable declared outside the lambda body. To illustrate that, first look at this single method interface: public interface MyFactory {
public String create(char[] chars);
}
Now, look this lambda expression that implements the MyFactory myFactory = (chars) -> {
return new String(chars);
};
Right now this lambda expression is only referencing the parameter value passed to it ( String myString = "Test";
MyFactory myFactory = (chars) -> {
return myString + ":" + new String(chars);
};
As you can see, the lambda body now references the local variable Instance Variable CaptureA lambda expression can also capture an instance variable in the object that creates the lambda. Here is an example that shows that: public class EventConsumerImpl {
private String name = "MyConsumer";
public void attach(MyEventProducer eventProducer){
eventProducer.listen(e -> {
System.out.println(this.name);
});
}
}
Notice the reference to
The semantics of Note: The above design of an event consumer is not particularly elegant. I just made it like that to be able to illustrate instance variable capture. Static Variable CaptureA Java lambda expression can also capture static variables. This is not surprising, as static variables are reachable from everywhere in a Java application, provided the static variable is accessible (packaged scoped or public). Here is an example class that creates a lambda which references a static variable from inside the lambda body: public class EventConsumerImpl {
private static String someStaticVar = "Some text";
public void attach(MyEventProducer eventProducer){
eventProducer.listen(e -> {
System.out.println(someStaticVar);
});
}
}
The value of a static variable is also allowed to change after the lambda has captured it. Again, the above class design is a bit nonsensical. Don't think too much about that. The class primarily serves to show you that a lambda can access static variables. Method References as LambdasIn the case where all your lambda expression does is to call another method with the parameters passed to the lambda, the Java lambda implementation provides a shorter way to express the method call. First, here is an example single function interface: public interface MyPrinter{
public void print(String s);
}
And here is an example of creating a Java lambda instance implementing the MyPrinter myPrinter = (s) -> { System.out.println(s); };
Because the lambda body only consists of a single statement, we can actually omit the enclosing MyPrinter myPrinter = s -> System.out.println(s);
Since all the lambda body does is forward the string parameter to the MyPrinter myPrinter = System.out::println;
Notice the double colons You can reference the following types of methods:
Each of these types of method references are covered in the following sections. Static Method ReferencesThe easiest methods to reference are static methods. Here is first an example of a single function interface: public interface Finder {
public int find(String s1, String s2);
}
And here is a static method that we want to create a method reference to: public class MyClass{
public static int doFind(String s1, String s2){
return s1.lastIndexOf(s2);
}
}
And finally here is a Java lambda expression referencing the static method: Finder finder = MyClass::doFind;
Since the parameters of the Parameter Method ReferenceYou can also reference a method of one of the parameters to the lambda. Imagine a single function interface that looks like this: public interface Finder {
public int find(String s1, String s2);
}
The interface is intended to represent a component able to search Finder finder = String::indexOf; This is equivalent of this lambda definition: Finder finder = (s1, s2) -> s1.indexOf(s2); Notice how the shortcut version references a single method. The Java compiler will attempt to match the referenced method against the first parameter type, using the second parameter type as parameter to the referenced method. Instance Method ReferencesThird, it is also possible to reference an instance method from a lambda definition. First, let us look at a single method interface definition: public interface Deserializer {
public int deserialize(String v1);
}
This interface represents a component that is capable of "deserializing" a
Now look at this public class StringConverter {
public int convertToInt(String v1){
return Integer.valueOf(v1);
}
}
The StringConverter stringConverter = new StringConverter(); Deserializer des = stringConverter::convertToInt;
The lambda expression created by the second of the two lines references the Constructor References
Finally it is possible to reference a constructor of a class. You do that by writing the class name followed
by MyClass::new Too see how to use a constructor as a lambda expression, look at this interface definition: public interface Factory {
public String create(char[] val);
}
The Factory factory = String::new; This is equivalent to this Java lambda expression: Factory factory = chars -> new String(chars); Next: Java Modules
Close TOC
All Trails Trail TOC Page TOC Previous Next | ||||||
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. | ||||||