lambda expressions in java (starting from beginners can understand)

Article Directory

History background

So far, passing a code snippet in Java is not easy, you can't pass the code snippet directly, Java is an object-oriented language, so you have to construct an object, the class of this object needs to have a method that can contain the required code

But in other languages ​​​​can directly process code blocks, Java refused to add this feature for a long time. We know the advantages of Java:

  • simplicity
  • consistency

If only one feature was added to make the code more concise, the language would quickly become a mess. (from Java Core Technology Volume 1)

In Java8, lambda expressions are proposed to process code blocks (become more concise and compact)

Syntax of lambda expressions

(parameter)->expression
    
(parameter)->{ sentence;  }
copy

feature:

  • Optional type declaration: There is no need to declare the parameter type, and the compiler can uniformly identify the parameter value.
  • Optional parameter parentheses: A parameter does not need to define parentheses, but multiple parameters need to define parentheses.
  • Optional curly braces: If the body contains a statement, curly braces are not required.
  • Optional return keyword: If the body has only one expression return value, the compiler will automatically return the value, curly braces need to specify that the expression returns a value.

specific writing

According to the characteristics mentioned above, lambda expressions can be roughly divided into the following three writing methods

No return value with formal parameters

package lambda;

/**
 * @Author Qiu Mingshan Code God
 * @Date 2022/12/21
 * @Description
 */
public class Test {
    public static void main(String[] args) {
        TestInterFace testInterFace = new TestInterFace() {
            @Override
            public void show(int a, int b) {
                System.out.println(a + b);
            }
        };
        testInterFace.show(30,40);

        TestInterFace testInterFace1 = (int a,int b)->{
            System.out.println(a+b);
        };

        TestInterFace testInterFace2 = (a,b) ->{
            System.out.println(a+b);
        };

        TestInterFace testInterFace3 = ((a, b) -> System.out.println(a+b));
    }
}
copy
package lambda;

/**
 * @Author Qiu Mingshan Code God
 * @Date 2022/12/21
 * @Description
 */
public interface TestInterFace {
    public abstract void show(int a,int b);
}
copy
  • The method name can be omitted, and the compiler will automatically detect the method name for you
  • The parameter type in the method can be omitted, and the compiler can deduce that a and b must be of type int
  • If there is only one line of implementation logic for the abstract method, the curly braces of the method body can be omitted

has a return value

package lambda;

/**
 * @Author Qiu Mingshan Code God
 * @Date 2022/12/21
 * @Description
 */
public class Test2 {
    public static void main(String[] args) {
        TestInterFace2 testInterFace = new TestInterFace2() {
            @Override
            public int add(int a, int b) {
                return a+b;
            }
        };
        
        TestInterFace2 testInterFace2 = (int a,int b) ->{
            return a-b;
        };
        
        TestInterFace2 testInterFace21 = (a,b) -> {return a-b;};
        
        TestInterFace2 testInterFace22 = (a,b) -> a-b;
    }
}
copy
package lambda;

/**
 * @Author Qiu Mingshan Code God
 * @Date 2022/12/21
 * @Description
 */
public interface TestInterFace2 {
    public abstract int add(int a,int b);
}
copy

Simplified description reference: no return value with formal parameters

has a parameter

When there is only one formal parameter, the parentheses of the formal parameter can be removed

package lambda;

/**
 * @Author Qiu Mingshan Code God
 * @Date 2022/12/21
 * @Description
 */
public class Test3 {
    public static void main(String[] args) {
        InterFace interFace = a -> System.out.println(a);
        interFace.show(10);
    }
}
copy
package lambda;

/**
 * @Author Qiu Mingshan Code God
 * @Date 2022/12/21
 * @Description
 */
public interface InterFace {
    public abstract void show(int a);
}
copy

functional interface

There are many interfaces that encapsulate code in Java, such as Comparator, and lambda expressions are compatible with these interfaces.

For an interface with only one abstract method, when an object of this interface is needed, a lambda expression can be provided. This interface is called a functional interface

lambda and Arrays.sort:

Note: the second parameter of sort requires a Compartor instance

package lambda;

import java.util.Arrays;
import java.util.Comparator;

/**
 * @Author Qiu Mingshan Code God
 * @Date 2022/12/21
 * @Description
 */
public class ArraySort {
    public static void main(String[] args) {
        // lambda way
        Integer[] numsArr = new Integer[10];
        for(int i = 0; i<10; i++){
            numsArr[i] = i;
        }
        /*
        Arrays.sort(numsArr,new Comparator<Integer>(){
            public int compare(Integer a, Integer b){
                return b-a;
            }
        });
        */
        Arrays.sort(numsArr, ( Integer a, Integer b) -> { return b-a;});
        for (int i: numsArr){
            System.out.println(i);
        }
    }
}
copy

In fact, in Java, all you can do with lambda expressions is convert to a functional interface. In other programming languages ​​that support function literals, you can declare function types (such as (String, String) -> int), declare variables of these types, and use variables to hold function expressions. However, the Java designers decided to keep the familiar interface concept and did not add function types to the Java language.

method reference

There may already be a ready-made method to help you complete an action that you want to pass to other codes. You can use a method reference, which is a bit abstract. In layman's terms: when the operation to be passed to the Lambda body, there is already an implementation method Now, you can use method references,

For example, I want to print the object of this event whenever a timer event occurs:

  1. Timer t = new Timer(1000,event -> System.out.println(event))
  2. Improvement: pass the println method directly to the Timer constructor Timer t = new Timer(1000,System.out::println);

To use the :: operator to separate the method name and the object or class name, there are three main cases

  • Object::InstanceMethod
  • class::static method
  • Class::InstanceMethod

In the first 2 cases, a method reference is equivalent to a lambda expression providing method parameters. As mentioned earlier, System.out::println is equivalent to x -> System.out.println(x). Similarly, Math::pow is equivalent to (x, y) -> Math.pow(x, y).

For case 3, the first parameter becomes the target of the method. For example, String::compareToIgnoreCase is equivalent to (x, y) -> x.compareToIgnoreCase(y).

Object::InstanceMethod

`Timer t = new Timer(1000,event -> System.out.println(event))`
copy

class::static method

(x,y) -> Math.pow(x, y)
copy

Class::InstanceMethod

package lambda;

import java.util.Comparator;

/**
 * @Author Qiu Mingshan Code God
 * @Date 2022/12/21
 * @Description
 */
public class Test4 {
    public static void main(String[] args) {
        Comparator<String> comparator = new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.compareTo(o2);
            }
        };

        System.out.println(comparator.compare("20", "2"));//1

        Comparator<String> comparator1 = String::compareTo;
        System.out.println(comparator1.compare("20", "2"));//1
    }
}
copy

constructor reference

Constructor references are similar to method references, except that the method name is new

ClassName::new

Assuming you have multiple methods, which one to construct? It depends on context, let's look at examples:

package lambda;

import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;

/**
 * @Author Qiu Mingshan Code God
 * @Date 2022/12/21
 * @Description
 */
public class constructor {
    public static void main(String[] args) {
        /*************** Constructor Reference ****************/
        // No-argument constructor, creating an instance
        Supplier<Emp> supper2 = () -> new Emp();
        Supplier<Emp> supper3 = Emp::new;
        Emp emp1 = supper3.get();
        emp1.setAddress("Shanghai");
        // a parameter
        Function<String, Emp> fun = address -> new Emp(address);
        Function<String, Emp> fun1 = Emp::new;
        System.out.println(fun1.apply("beijing"));
        // two parameters
        BiFunction<String, Integer, Emp> bFun = (name, age) -> new Emp(name, age);
        BiFunction<String, Integer, Emp> bFun1 = Emp::new;
        System.out.println(bFun1.apply("xiaohong", 18));

    }


    static class Emp {
        private String address;

        private String name;

        private Integer age;

        public Emp() {

        }

        public Emp(String address) {
            this.address = address;
        }

        public Emp(String name, Integer age) {
            this.name = name;
            this.age = age;
        }

        public Emp(String address, String name, Integer age) {
            super();
            this.address = address;
            this.name = name;
            this.age = age;
        }

        public String getAddress() {
            return address;
        }

        public void setAddress(String address) {
            this.address = address;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Integer getAge() {
            return age;
        }

        public void setAge(Integer age) {
            this.age = age;
        }

        @Override
        public String toString() {
            return "Emp [address=" + address + ", name=" + name + ", age=" + age + "]";
        }

}
copy

variable scope

lambda expression:

  1. a code block
  2. parameter
  3. The value of a free variable, which refers to a variable that is not a parameter and is not defined in the code

There's a term for code blocks and free variable values: closures, and if anyone brags that their language has closures, now you can confidently say that Java has closures too. In Java, lambda expressions are closures.

A lambda expression can capture the value of a variable in the enclosing scope. In Java, there is an important restriction on ensuring that the captured value is well-defined. In lambda expressions, only variables whose values ​​do not change can be referenced.

public static void countDown(int start, int delay)
{
	ActionListener listener = event ->
	{
		start--; // Error: Can't mutate captured variable
		System.out.println(start);
	};
	new Timer(delay, listener).start();
}
copy

If you change variables in a lambda expression, it is not safe to execute multiple actions concurrently.

It is also illegal to refer to a variable in a lambda expression that might change externally. For example, the following is illegal:

public static void repeat(String text, int count)
{
	for (int i = 1; i <= count; i++)
	{
		ActionListener listener = event ->{
            System.out.println(i + ": " + text);// Error: Cannot refer to changing i
        }
    }
	new Timer(1000, listener).start();
}
copy

Rules: 1. Variables captured in lambda expressions must actually be final variables

After this variable is initialized, it will not be assigned a new value.

  1. It is not legal to declare a parameter or local variable with the same name as a local variable in a lambda expression.

Tags: Java IDE serverless

Posted by x2fusion on Thu, 22 Dec 2022 07:21:59 +0530