To execute a thread in Java, use a subclass that inherits the Thread class, and There is a method using a subclass that implements the Runnable interface. This time, the latter is taken as an example for a brief explanation of the lambda expression.
Function.java
public class Function implements Runnable{
@Override
public void run() {
//What to process in a thread
System.out.println("hello!");
}
}
Main.java
public class Main {
public static void main(String[] args) {
Thread thread = new Thread(new Function());
thread.start();
System.out.println("finish!");
}
}
I think the output will look like this.
finish!
hello!
What is the relationship between the Thread class and the Runnable interface? The Strategy pattern design of the GoF design pattern is used. Roughly speaking, the Strategy pattern is designed to "realize a replaceable algorithm". The class that was originally one is in charge of the overall flow of processing, Let's divide it into classes that are in charge of specific algorithms. By using the Strategy pattern, with the user (Thread class) The side to be used (the class that realizes the Runnable interface) Since it does not have to be directly related, it is possible to realize an algorithm that can be replaced later. (For those who want to know more about Strategy patterns, see here) However, the class that realized the interface is complicated (FunctionHard class in UML diagram). There is something like a class that requires simple code (FunctionEasy class in UML diagram).
New classes one by one, even with simple code Isn't it annoying to have to define? about it.
In order to solve the problem presented in the introduction, we will use the lambda expression introduced from Java SE 8. The following code replaces it with a lambda expression instead of creating a FunctionEasy class.
Lambda.java
public class Lambda {
public static void main(String[] args) {
Runnable r = () -> System.out.println("hello!");
Thread thread = new Thread(r);
thread.start();
System.out.println("finish!");
}
}
You only need one line like this. Refreshing! Lambda expressions are "when instantiating a functional interface You can think of it as "a notation that does not require complicated writing."
An interface that has only one method that needs to be implemented is called a "functional interface" It is also called "SAM (Single Abstarct Method) interface".
As you can see from SAM, it has only one abstract method, so Which method does the lambda expression implement? What about arguments and return values? Inference is possible. There is no need to decide the method name one by one. Polymorphism can be realized without preparing a class that realizes the interface.
Before Java8, it was realized by an anonymous class. Anonymous classes are sometimes called anonymous classes.
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("hello!");
}
}
It's long and unreadable.
With the advent of lambda expressions in Java8, it's easier to write them.
Runnable r = () -> {
System.out.println("hello!");
}
A lambda expression declaration consists of an argument variable declaration and a processing block as shown below.
(argument) -> {processing; };
"->" Is called an arrow operator.
Some lambda expressions can be omitted. Let's follow the following prototype as an example.
prototype
Sample sample = (String a) -> { System.out.println(a);};
If there is only one argument **, the parentheses "()" can be omitted. Also, the argument type can be inferred, so it can be omitted.
Abbreviation 1
Sample sample = a -> { System.out.println(a);};
Also, if the method body is one line, you can omit the curly braces "{}" and the semicolon ";" at the end of the sentence. In the case of return statement, return can also be omitted.
Abbreviation 2
Sample sample = a -> System.out.println(a);
In addition, if the argument can be inferred, the method call will Class name :: method name Object name :: method name It can be omitted like this.
Abbreviation notation 3
Sample sample = System.out::println;
** A variable with the same name as the local variable declared in the method cannot be used as the argument name of the lambda expression. ** ** The following will result in a compile error.
public class Sample {
public static void main(String[] args) {
String a = "sample";
Function f = a -> System.out.println(a); //error
//Abbreviation
}
}
Since the variable name a has already been declared as a local variable, it cannot be used as an argument name for a lambda expression.
** To access a local variable declared outside a lambda expression from within the lambda expression, the local variable must be final. If it does not have the final modifier, it must be practically final. ** **
You can access the local variables of the method that surrounds the expression from the lambda expression. (That's what it looks like before it's converted to a lambda expression.) You can access the local variables of the method that declares the expression from within the lambda expression as follows:
"Substantially final" means a variable that does not change even if it is not qualified with final. If you change the value of a local variable in a lambda expression as shown below, a compile error will occur.
public class Sample {
public static void main(String[] args) {
String a = "sample";
Function f = () -> {
a = "change"; //error
System.out.println(a);
};
//Abbreviation
}
}
Frequently used functional interfaces are in the java.util.function package. In particular, the following five functional interfaces are famous.
Functional interface | Method | argument | Return value | Description |
---|---|---|---|---|
Consumer<T> | void accept(T) | Yes | None | "Consumer" who does not return a value |
Supplier<T> | T get() | None | Yes | "Supplier" returning a value |
Predicate<T> | boolean test(T) | Yes | Yes | "Affirmation" that returns a boolean value |
Function<T, R> | R apply(T) | Yes | Yes | "Processing" that receives an argument and returns the result of the specified type |
UnaryOperator<T> | T apply(T t) | There are two | Yes | "Operation" that returns the operation result |
The Collection API has many default methods for manipulating List and Map.
For example, java.util.List has a method called * removeIf (Predicate filter) * to remove the element that matches the filter. If you use a lambda expression, you only need one line.
RemoveIf.java
import java.util.ArrayList;
import java.util.Arrays;
public class RemoveIf {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>(Arrays.asList("aaaa", "bbb", "cc"));
list.removeIf(v -> v.length() > 3);
System.out.println(list); //result: [bbb, cc]
}
}
There are many other methods that allow you to specify a lambda expression. Let's investigate by yourself and implement it with a lambda expression to get used to it.
There are many web applications that run on Java 8 on the server side. Therefore, I think that the lambda expression & Stream API is inevitable knowledge when doing Java now. Nevertheless, it seems that there are some things that are not taught in Java newcomer training, so I tried to summarize it as easily as possible for newcomers. I'm sorry if there is a part that is difficult to understand.
Next time, I will summarize the introduction to Stream API around the next week.
(Updated December 22, 2019) Sequel Something → [Java] Introduction to Stream API
Recommended Posts