New Java Features: Lambda Expressions

New Java Features: Lambda Expressions

Lambda expression (Lambda expression), also known as closure (Closure), is an important new feature in Java (SE) 8. Lambda expressions allow us to pass expressions in place of functional interfaces. Lambda expressions are just like methods, which provide a normal parameter list and a body (body, which can be an expression or a code block) that uses these parameters

Lambda expression can be regarded as an anonymous function, named after the lambda calculus in mathematics.



1. Overview of Lambda expressions

1.1 Introduction to Lambda expressions

Lambda expression (Lambda expression), also known as closure (Closure), is an important new feature in Java (SE) 8.

Lambda expressions allow us to pass expressions in place of functional interfaces. Lambda expressions are just like methods, which provide a normal parameter list and a body (body, which can be an expression or a code block) that uses these parameters

Lambda expression can be regarded as an anonymous function, named after the lambda calculus in mathematics.

1.2. The syntax of Lambda expressions

The basic syntax of Lambda expressions:

(parameters) -> expression 或 (parameters) -> {
    
     statements; }

The three components of a lambda expression:

image-20230707105839045

  1. Parameter list (parameters): Similar to the formal parameter list in the method, the parameters here are the parameters in the functional interface. The parameter types here can be explicitly declared, or not declared but implicitly inferred by the JVM. In addition, parentheses can be omitted when there is only one inferred type;
  2. Arrow (->): connects the parameter list and the Lambda body, which can be understood as "used for";
  3. Lambda body (expression or {statements; }): It can be an expression or a code block, and it is the implementation of a method in a functional interface. The code block can return a value or return nothing, where the code block block is equivalent to the method body of the method. If it is an expression, it can also return a value or nothing.

Sample Lambda expression:

        // 1. 不需要参数,返回值为 2
        () -> 2
				// 2. 接收一个参数(数字类型),返回其2倍的值
        x -> 2*x
				// 3. 接受2个参数(数字),并返回他们的和
        (x, y) -> x+y
				// 4. 接收2个int型整数,返回他们的乘积
        (int x, int y) -> x*y
				// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
        (String s) -> System.out.print(s)
1.3, Lambda expression requirements

Although, Lambda expressions can simplify the implementation of interfaces to a certain extent. However, not all interfaces can be implemented concisely using Lambda expressions.

A lambda expression is just an anonymous method after all. When there are too many or too many methods in the implemented interface, Lambda expressions are not applicable.

Lambda expressions can only implement functional interfaces.


2. Functional interface

2.1. What is a functional interface

If we say that in an interface, there is one and only one abstract method that the implementing class must implement! Such an interface is a functional interface.

The code is as follows (example):

// 有且只有一个实现类必须要实现的抽象方法,所以是函数式接口
interface Demo{
    
    
    public void action();
}

or (example):

// 有且只有一个实现类必须要实现的抽象方法,所以是函数式接口
interface Demo{
    
    
    public void action();
    public default void action2() {
    
    
        System.out.println("JDK1.8新特性,default默认方法可以有具体的实现");
    }
}
2.2. Functional Interface Annotation (@FunctionalInterface)

Before the interface, we can use @FunctionalInterfaceannotations to determine whether the interface is a functional interface. , if it is a functional interface, there is no problem. If it is not a functional interface, an error will be reported. Functionally similar to @Override.
The code is as follows (example):

@FunctionalInterface
interface Demo{
    
    
    public void action();
}

3. The use of Lambda expressions

3.1. Basic use of Lambda expressions

Example 1 (no parameters and no return value):

@FunctionalInterface
interface NoParameterNoReturn {
    
    
    // 注意:只能有一个抽象方法
    void action();
}
public class TestDemo {
    
    
    public static void main(String[] args) {
    
    
        NoParameterNoReturn noParameterNoReturn = () -> {
    
    
            System.out.println("无参数无返回值");
        };
        // action方法的主体内容在上述括号内
        noParameterNoReturn.action(); 
     }
}

Example 2 (one parameter has no return value):

@FunctionalInterface
interface OneParameterNoReturn {
    
    
  	// 注意:只能有一个抽象方法
    void action(int a);
}
public class TestDemo {
    
    
    public static void main(String[] args) {
    
    
        OneParameterNoReturn oneParameterNoReturn = (int a) -> {
    
    
            System.out.println("一参数无返回值:" + "参数1:" + a);
        };
        // action方法的主体内容在上述括号内
        oneParameterNoReturn.action(10);
     }
}

Example 3 (multiple parameters without return value):

@FunctionalInterface
interface MoreParameterNoReturn {
    
    
  	// 注意:只能有一个抽象方法
    void action(int a, int b);
}
public class TestDemo {
    
    
    public static void main(String[] args) {
    
    
						MoreParameterNoReturn moreParameterNoReturn = (int a, int b) -> {
    
    
            System.out.println("多参数无返回值:" + "参数1:" + a  + "参数2:" + b);
        };
        moreParameterNoReturn.action(20, 30);
     }
}

Example 4 (no parameter and return value):

@FunctionalInterface
interface NoParameterReturn {
    
    
    int action();
}
public class TestDemo {
    
    
    public static void main(String[] args) {
    
    
        NoParameterReturn noParameterReturn = () -> {
    
    
            System.out.println("无参数有返回值");
            return 40;
        };
        //接收函数的返回值
        int ret = noParameterReturn.action();
        System.out.println(ret);
     }
}

Example 5 (one parameter has a return value):

//有返回值一个参数
@FunctionalInterface
interface OneParameterReturn {
    
    
    int action(int a);
}
public class TestDemo {
    
    
    public static void main(String[] args) {
    
    
        OneParameterReturn oneParameterReturn = (int a) -> {
    
    
            System.out.println("一参数有返回值");
            return a;
        };
        ret = oneParameterReturn.action(50);
        System.out.println(ret);
     }
}

Example 6 (multiple parameters have return values):

@FunctionalInterface
interface MoreParameterReturn {
    
    
    int action(int a, int b);
}
 
public class TestDemo {
    
    
    public static void main(String[] args) {
    
    
        MoreParameterReturn moreParameterReturn = (int a, int b) -> {
    
    
            System.out.println("多参数有返回值");
            return a + b;
        };
        ret = moreParameterReturn.action(60, 70);
        System.out.println(ret);
     }
}
3.2. Syntax simplification of Lambda expressions

Syntax condensed for lambda expressions:

  • The parameter type can be omitted. If it needs to be omitted, the type of each parameter must be omitted;
  • There is only one parameter in the parentheses of the parameter, so the parentheses can be omitted;
  • If there is only one sentence of code in the method body, the braces can be omitted;
  • If there is only one statement in the method body, which is a return statement, then the curly braces can be omitted, and the return keyword can be removed.

4. Variable capture

There are variable captures in Lambda expressions. After understanding variable captures, we can better understand the scope of Lambda expressions. In anonymous classes in Java, there will be variable capture.

4.1. Variable capture of anonymous inner class

The so-called variable capture is: accessing local variables in the method or code block in the anonymous inner class, this process is called "variable capture".

public static void main(String[] args) {
    
    
    // 变量捕获
    int a = 100;
    NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn() {
    
    
        @Override
        public void action() {
    
    
            // 捕获a变量
            System.out.println(a);   
        }
    };
    noParameterNoReturn.action();
}
结果:100

However, anonymous inner classes can only reference a local variable if it is "effectively final". If a local variable is not effectively final, it cannot be referenced in an anonymous inner class, resulting in a compilation error.

An effectively final variable refers to a local variable that has not been modified during its lifetime and can be considered a constant.

4.2, Lambda variable capture

Likewise, Lambda's variable capture is only able to reference a local variable if it is "effectively final".

public static void main(String[] args) {
    
    
    // 变量捕获
    int a = 100;
    NoParameterNoReturn noParameterNoReturn = ()->{
    
    
        // 捕获a变量
        System.out.println(a);
    };
    noParameterNoReturn.action();
}
结果:100
4.3. The difference between the variable capture of anonymous inner class and the variable capture of Lambda

Difference: Lambda expressions can capture the outer this, while anonymous inner classes cannot directly capture the outer this.

In a Lambda expression, the this keyword refers to the instance of the class in which the Lambda expression resides, not the instance of the class inside the Lambda expression. This feature is called "closure".

The this keyword in an anonymous inner class refers to an instance of the anonymous inner class, not an instance of the outer class.

public class Student {
    
    
    // 外部类的属性
    String name = "Student";
    int age;
 
    NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn() {
    
    
        // 内部类自己的属性
        String name = "hello";
        @Override
        public void action() {
    
    
            System.out.println(this.name);
        }
    };
 
    NoParameterNoReturn noParameterNoReturn2 = ()->{
    
    
        String name = "hello";
        System.out.println(this.name);
    };
 
    public static void main(String[] args) {
    
    
        Student student = new Student();
      	// 结果:hello
        student.noParameterNoReturn.action();
      	// 结果:Student
        student.noParameterNoReturn2.action();
    }
}

If you want the anonymous inner class to capture the outside this, you need to use the class name or object reference of the outer class to access.

public class Student {
    
    
    // 外部类的属性
    String name = "Student";
    int age;
 
     NoParameterNoReturn noParameterNoReturn = new NoParameterNoReturn() {
    
    
        // 内部类自己的属性
        String name = "hello";
        @Override
        public void action() {
    
    
            System.out.println(Student.this.name);
        }
    };
}
结果:Student

5. The use of Lambda in collections

In order to allow Lambda to work better with Java collection classes, some interfaces have been added to the collection to connect with Lambda expressions.

corresponding interface new method
Collection forEach() removeIf() spliterator() stream() parallelStream()
List replaceAll() sort()
Map getOrDefault() forEach() replaceAll() putIfAbsent() remove() replace() computeIfAbsent() computeIfPresent() compute() merge()
5.1. The forEach() method in the Collection interface

This method is in the interface Iterable:

    default void forEach(Consumer<? super T> action) {
    
    
        Objects.requireNonNull(action);
        for (T t : this) {
    
    
            action.accept(t);
        }
    }

This method is used to perform the specified operation on each element in the collection.

java复制代码public class Test {
    
    
    public static void main(String[] args) {
    
    
        List<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("bit");
        list.add("hello");
        list.add("lambda");
        
        // Lambda 遍历 list
        list.forEach((String s)->{
    
    
            System.out.println(s);
        });
      
      	// 简化
      	// list.forEach(s -> System.out.println(s));
    }
}
结果:Hello bit hello lambda
5.2. The sort() method in the List interface

The sort() method in the List interface:

    default void sort(Comparator<? super E> c) {
    
    
        Object[] a = this.toArray();
        Arrays.sort(a, (Comparator) c);
        ListIterator<E> i = this.listIterator();
        for (Object e : a) {
    
    
            i.next();
            i.set((E) e);
        }
    }

This method sorts the elements of the container according to the comparison rules specified by c. You can see that its parameter is Comparator, let’s click in and have a look: it is another functional interface:

@FunctionalInterface
public interface Comparator<T> {
    
    
		...
    int compare(T o1, T o2);
    ...
}

Example usage:

public class TestDemo2 {
    
    
    public static void main(String[] args) {
    
    
        ArrayList<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("bit");
        list.add("hello");
        list.add("lambda");

        /*
        对list集合中的字符串按照长度进行排序
         */
        list.sort(new Comparator<String>() {
    
    
            @Override
            public int compare(String o1, String o2) {
    
    
                return o1.length() - o2.length();
            }
        });

        /*
        输出排序后最终的结果
         */
        list.forEach(s -> System.out.println(s));
    }
}
输出结果为:bit Hello hello lambda

Modified to Lambda expression:

public class TestDemo2 {
    
    
    public static void main(String[] args) {
    
    
        ArrayList<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("bit");
        list.add("hello");
        list.add("lambda");

        // 对list集合中的字符串按照长度进行排序
        list.sort((o1, o2) -> o1.length() - o2.length());
        list.forEach(s -> System.out.println(s));
    }
}
输出结果为:bit Hello hello lambda
5.3. The forEach() method of the Map interface

forEach() method of HashMap:

    default void forEach(BiConsumer<? super K, ? super V> action) {
    
    
        Objects.requireNonNull(action);
        for (Map.Entry<K, V> entry : entrySet()) {
    
    
            K k;
            V v;
            try {
    
    
                k = entry.getKey();
                v = entry.getValue();
            } catch(IllegalStateException ise) {
    
    
                // this usually means the entry is no longer in the map.
                throw new ConcurrentModificationException(ise);
            }
            action.accept(k, v);
        }
    }

You can see that this method has a BigConsumer, and this is also a functional interface:

@FunctionalInterface
public interface BiConsumer<T, U> {
    
    
		...
    void accept(T t, U u);
    ...
}

Code example:

public class TestDemo2 {
    
    
    public static void main(String[] args) {
    
    
        HashMap<Integer, String> map = new HashMap<>();
        map.put(1, "hello");
        map.put(2, "bit");
        map.put(3, "hello");
        map.put(4, "lambda");

        map.forEach(new BiConsumer<Integer, String>() {
    
    
            @Override
            public void accept(Integer integer, String string) {
    
    
                System.out.println(integer + " " + string);
            }
        });

    }
}
输出结果:
1 hello
2 bit
3 hello
4 lambda

Modified to Lambda expression:

public class TestDemo2 {
    
    
    public static void main(String[] args) {
    
    
        HashMap<Integer, String> map = new HashMap<>();
        map.put(1, "hello");
        map.put(2, "bit");
        map.put(3, "hello");
        map.put(4, "lambda");

        map.forEach((integer, string) ->
                System.out.println(integer + " " + string)
        );
    }
}

Guess you like

Origin blog.csdn.net/weixin_45187434/article/details/131622541