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 value1123457⑤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:
  1. 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

  2. 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)