Article Directory
- History background
- Syntax of lambda expressions
- specific writing
- functional interface
- method reference
- constructor reference
- variable scope
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
copy(parameter)->expression (parameter)->{ sentence; }
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
copypackage 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)); } }
copypackage lambda; /** * @Author Qiu Mingshan Code God * @Date 2022/12/21 * @Description */ public interface TestInterFace { public abstract void show(int a,int b); }
- 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
copypackage 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; } }
copypackage lambda; /** * @Author Qiu Mingshan Code God * @Date 2022/12/21 * @Description */ public interface TestInterFace2 { public abstract int add(int a,int b); }
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
copypackage 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); } }
copypackage lambda; /** * @Author Qiu Mingshan Code God * @Date 2022/12/21 * @Description */ public interface InterFace { public abstract void show(int a); }
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
copypackage 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); } } }
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:
- Timer t = new Timer(1000,event -> System.out.println(event))
- 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
copy`Timer t = new Timer(1000,event -> System.out.println(event))`
class::static method
copy(x,y) -> Math.pow(x, y)
Class::InstanceMethod
copypackage 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 } }
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:
copypackage 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 + "]"; } }
variable scope
lambda expression:
- a code block
- parameter
- 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.
copypublic 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(); }
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:
copypublic 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(); }
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.
- It is not legal to declare a parameter or local variable with the same name as a local variable in a lambda expression.