Explain Lambda expressions, functional interfaces, and method references
Lambda expressions
1. Introduction to Lambda expressions
Lambda expressions are functional programming that passes behavior that can be executed once or more later. Make write more concise, flexible, compact code.
2. Optimizations using Lambda
When a Thread needs to be started to complete a task, the task content is typically defined through the java.lang.Runnable interface and the java.lang.Thread class is used to start the Thread.
The traditional way of writing it is as follows:
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run(a) {
System.out.println("Execute thread task");
}
}).start();
}
Copy the code
We can see what we really want to do: pass the code inside the run method to the Thread class.
The function is passed as an argument to a method.
Lambda expression:
Thanks to Java8’s new syntax, the anonymous inner class writing of the Runnable interface above can be equivalent with simpler Lambda expressions
public static void main(String[] args) {
new Thread(() -> System.out.println("Execute thread task")).start();
}
Copy the code
This code is exactly the same as what we just did. You can see that we started a thread, and the contents of the thread task are specified in a more concise form.
3. The Lambda format
The Lambda format consists of three parts
The standard format is:
(Parameter Type Parameter Name...) -> {code statement}Copy the code
- The syntax inside some argument parentheses is the same as in traditional method argument lists: empty if no arguments are present; Multiple parameters are separated by commas.
- An arrow
->
Is a newly introduced syntactic format that represents a pointing action. - The syntax inside curly braces of a piece of code is basically the same as the traditional method body. The implementation is the logical part of the code, which can be a line of code or a code fragment
Abbreviated format
When the compiler can automatically deduce the parameter types and return values of this method, it can be omitted.
1. Parameter types in parentheses can be omitted
(Parameter name…) -> {code statement}
public static void main(String[] args) {
Comparator<String> comparator;
/ / the original writing
comparator = new Comparator<String>() {
@Override
public int compare(String first, String second) {
int lenNum = first.length() - second.length();
if (lenNum > 0) return 1;
if (lenNum < 0) return 0;
return 0; }};/ / Lambda method
comparator = (first, second) -> {
int lenNum = first.length() - second.length();
if (lenNum > 0) return 1;
if (lenNum < 0) return 0;
return 0;
};
}
Copy the code
2. If in parenthesesOne and only one parameter, the parentheses can be omitted;
Parameter Name -> {code statement}
public static void main(String[] args) {
Comparable comparable;
/ / the original writing
comparable = new Comparable() {
@Override
public int compareTo(Object o) {
if (null == o) {
return 0;
}
return 1; }};/ / Lambda method
comparable = o -> {
if (null == o) {
return 0;
}
return 1;
};
}
Copy the code
3. If in bracesThere is and only one statement, you can omit the braces, return keyword, and statement semicolon regardless of whether a value is returned.
(Parameter Type Parameter Name…) -> Code statement
public static void main(String[] args) {
Comparator<String> comparator;
/ / the original writing
comparator = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
returno1.length() - o2.length(); }};/ / Lambda method
comparator = (first, second) -> first.length() - second.length();
}
Copy the code
4. To summarize
Lambda expressions cannot stand alone and will always be converted to instances of functional interfaces.
2. Functional interfaces
1. What are functional interfaces
A function interface is an interface that has one and only abstract method, be it a Runnable, Comparator, or custom interface. It can only be used as a Lambda expression if the abstract method in the interface exists and is unique.
2.Java 8 often uses functional interfaces
Functional interface | Input parameter type | The return type | Abstract method name | describe | The sample |
---|---|---|---|---|---|
Supplier | There is no | T | get | Provide a value of type T | Factory like method |
Consumer | T | void | accept | Handles a value of type T | Used as a print entry parameter |
Function<T, R> | T | R | apply | A function that takes a parameter of type T | Gets the name of person |
UnaryOperator | T | T | apply | Unary operation on type T | The input number is incremented |
BinaryOperator | T,T | T | apply | A binary operation on type T | Multiply two numbers in the input |
Predicate | T | boolean | test | Boolean valued function | Determine if person is older than 18 |
Example:
static class Person {
private String name;
private Integer age;
// Ignore get set toString
}
public static void main(String[] args) {
// Return a Person object
Supplier<Person> supplier = () -> new Person("Prosperous wealth.".18);
// Used as a print entry parameter
Consumer<String> consumer = s -> System.out.println(s);
// Get the name of the person object
Function<Person, String > function = person -> person.getName();
// The input number is incremented by 1
UnaryOperator<Integer> unaryOperator = i -> ++i;
// Multiply the input two numbers
BinaryOperator<Integer> binaryOperator = (x, y) -> x * y;
// Determine if the person object is older than 18
Predicate<Person> predicate = person -> person.getAge() > 18;
Person person = new Person("Prosperous wealth.".18);
System.out.println("(1) Supplier ==> create person object:" + supplier.get());
System.out.print("② Consumer ==> print input parameter:"); consumer.accept("Hey, Lennar, sit down.");
System.out.println(③function ==> get the name of the person object: + function.apply(person));
System.out.println("④unaryOperator ==> The input value increases by 1:" + unaryOperator.apply(123456));
System.out.println("⑤binaryOperator ==> multiply two input values:" + binaryOperator.apply(1234.5678));
System.out.println(⑥ Predicate ==> Determines whether the person object is older than 18: + predicate.test(person));
}
Copy the code
Results:
(1) Supplier ==> create a person object: person (name= rich, age=18③function ==> Get the name of the person object; ④unaryOperator ==> Increment the input value1 :123457⑤binaryOperator ==> Multiply two values of input:7006652⑥ Predicate ==> Determines whether the person object is older or not18Age:false
Copy the code
3. Custom functional interface
Is actually a custom interface with one and only abstract method.
You can use the @functionalInterface annotation to modify the desired interface, and the compiler checks if the interface has only one abstract method; otherwise, an error is reported. You can have multiple default methods, static methods.
define
@FunctionalInterface
interface TestInterface {
void getMaxNum(Integer a, Integer b);
}
Copy the code
The sample
public static void main(String[] args) {
TestInterface testInterface = (a, b) -> a >= b ? a : b;
Integer maxNum = testInterface.getMaxNum(255.Awesome!);
System.out.println("MaxNum:" + maxNum);
/ / 666
}
Copy the code
Method reference
1. Brief introduction of method references
Method references are shorthand for existing methods in Lambda expressions. Lambda expressions can be rewritten as method references that directly access existing methods or constructors of classes or instances using the :: operator.
Method references can be thought of as another representation of a Lambda expression.
In layman’s terms, what Lambda overrides can be used just like any other existing method body.
2. Use conditions for method references
-
The parameter list of the referenced method is the same as the parameter list of the abstract method in the functional interface
-
The interface’s abstract methods have no return value, and the referenced methods may or may not have a return value
-
The interface’s abstract methods have return values, and the referenced methods must have the same type of return value
3. The type of method reference
type | grammar | Corresponding Lambda expression |
---|---|---|
Static method reference | The name of the class: : staticMethod | -> Class name. StaticMethod (args) |
Instance method reference | inst::instMethod | (args) -> inst.instMethod(args) |
Object method reference | The name of the class: : instMethod | (inst,args) -> Name of the inst class. |
Build method reference | The name of the class: : new | (args) -> New class name (args) |
1. Static method references
public static void main(String[] args) {
T apply(T T, T u);
BinaryOperator<Integer> binaryOperator = (a, b) -> (a >= b) ? a : b;
}
Copy the code
Suppose we want to implement a BinaryOperator whose logical content outputs the largest number.
So we can rewrite this as
public static void main(String[] args) {
BinaryOperator<Integer> binaryOperator = (a, b) -> (a >= b) ? a : b;
// These two are equivalent
binaryOperator = Math::max;
}
Copy the code
We’re actually referring to static methods under the Math class
public static int max(int a, int b) {
return (a >= b) ? a : b;
}
Copy the code
We realized that the comparison method we were going to implement already had a ready-made logical content structure in the Math class, so we just referenced it and used it.
Where Math:: Max is equivalent to (a, b) -> (a >= b)? a : b
2. Instance method references
Here we create a class for reference and the corresponding method.
class TestInstanceFun {
public int isNull(Object obj) {
if (null == obj)
return 0;
else
return 1; }}Copy the code
Specific call
public static void main(String[] args) {
Comparable comparable = o -> {
if (null == o) {
return 0;
} else {
return 1; }}; TestInstanceFun testInstanceFun =new TestInstanceFun();
comparable = testInstanceFun::isNull;
}
Copy the code
The Comparable logic we want to implement here is to return 0 when the object is NULL and 1 otherwise,
This is similar to static method references, which can be referenced using an instance of TestInstanceFun.
3. object method references
Special conditions:
-
There must be at least one input parameter, and only the method of that input parameter must be called in the method body without doing anything else
-
The second argument starts with a list of incoming arguments to the method being called.
Here we start by creating a functional interface for testing
@FunctionalInterface
interface TestInterface {
simpleSubString(String originalString, int a, int b);
}
Copy the code
Specific call
public static void main(String[] args) {
// Suppose the logic is to cut the end of the string.
TestInterface testInterface = (originalString, a, b) -> originalString.substring(a, b);
// Here we can convert object method references that use String
testInterface = String::substring;
}
Copy the code
The subString (int beginIndex, int endIndex) method of the String referenced is
public String substring(int beginIndex, int endIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
if (endIndex > value.length) {
throw new StringIndexOutOfBoundsException(endIndex);
}
int subLen = endIndex - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return ((beginIndex == 0) && (endIndex == value.length)) ? this : new String(value, beginIndex, subLen);
}
Copy the code
The originalString of the first input calls its subString method, and the following two arguments, a and B, are inputs to subString and can therefore be converted to String::substring, Originalstring.substring (a, b).
4. Construct method references
A constructor reference is similar to a method reference, except that the method name is new. The referenced class must have a constructor consistent with the abstract method argument list of the functional interface, and return arguments that are instances of the class.
The test class
class Person {
String name;
String age;
public Person(a) {
this.name = "default";
this.age = "18";
}
public Person(String name, String age) {
this.name = name;
this.age = age; }}Copy the code
Test the functional interface
@FunctionalInterface
interface TestStructureFun{
Person getPerson(String a, String b);
}
Copy the code
Specific call
public static void main(String[] args) {
TestStructureFun testStructureFun = (a, b) -> new Person(a, b);
// We can write it this way
testStructureFun = Person::new;
}
Copy the code
Here the Person::new is a reference to the Person constructor. It is inferred from the context that we are referring to the constructors of the two arguments in Person.
4. Conclusion:
Method references cannot stand on their own and are always converted to instances of functional interfaces.
If there are multiple overloaded methods with the same name, the compiler will try to figure out which method you are referring to from the context.
For example, in the static method reference example in this article:
The math.max method comes in four versions, one for integers and one for double values. Which version you choose depends on the method parameters of the functional interface to which Math:: Max is converted.
Public static int Max (int a, int b) public static int Max (int a, int b)