Write in front
- When talking about the new features of Java 8, our first thought must be the emergence of Lambda expressions and functional interfaces. To say, has ta "optimized" the simplicity of the code to a certain extent? Or ta to some extent, it increases the difficulty of reading and debug ging for programmers, which gives many programmers a headache. In this issue, we will continue to "talk about Java", and the new feature is only Lambda, who loves and hates.
Lambda expression
In essence, it belongs to the concept of functional programming, which can return the implementation of an interface
Application in thread
Traditional way
Create a one-time class
//A one-time class used as the implementation class of Runnable pair in new Thread class runnable implements Runnable{ @Override public void run() { System.out.println("I am on the way"); } } public class lambdaTest { public static void main(String[] args) { runnable runnable = new runnable(); Thread thread1 = new Thread(runnable); } }
(slightly optimized) anonymous inner class
Thread thread1 = new Thread(new Runnable() { @Override public void run() { System.out.println("I am on the way"); } });
effect
- Avoid too many anonymous inner class definitions
- Make the code look simple
- Simplify the code, leaving only the core logic
Functional interface
Definition: any interface that contains only one abstract method is a functional interface
public interface Runnable{ void run(); }
In order to avoid that the interface has multiple functions and is no longer a functional interface after later people add functions to the interface, we can declare @ FunctionalInterface above the interface class
All Lambda types are an interface
- The Lambda expression itself is the implementation of this interface
If it is defined as an implementation class, an error will be reported
- When compiling the program, we don't know which subclass of the object we finally create. We can't know it until the runtime. Then we can make this like point to different classes and call different subclass methods, which greatly improves the scalability of the program
Here, lambda is actually equivalent to an anonymous internal class (without class name). We don't know what the class actually created is, so we will define it as an interface and use the polymorphic upward transformation feature
For more features of polymorphism, in my other blog: Portal - > polymorphic
Method reference
Demo
//Interface definition interface parseIntNum{ //Define a method to convert String to Integer int pass(String s); } public static void main(String[] args) { parseIntNum parseIntNum1; parseIntNum parseIntNum2; //Original lambda parseIntNum1 = (str)-> Integer.parseInt(str); System.out.println(parseIntNum1.pass("1")); //Method reference improved version parseIntNum2 = Integer::parseInt; System.out.println(parseIntNum2.pass("1")); }
The so-called method reference means that if the signature of an existing method is exactly the same as the function defined in the interface, the method reference can be directly passed in.
Because the method defined by the parseIntNum interface is int pass(String s), compared with the static method int parseInt(String s) in Integer, except for the method name, the method parameters are the same and the return type is the same, which means that the method signature is the same and can be directly passed into the method reference
Method reference
- In fact, it is very simple. You only need to use the operator double colon * * "::"**
Common method references
Common forms of reference
Class name:: static method name
Calling a static method of a class
- In fact, using Integer::parseInt above is equivalent to calling Integer's static method parseInt
Objects: instance methods
Here is a little bit hhhh
class Naruto{ public static void Rasengan(){ System.out.println("Spiral pill"); } } //Interface definition interface Ninja{ //Define an esoteric method void aoyi(); } public class methodQuote { //By quoting Naurto's spiral pill Ninja ninja=Naruto::Rasengan; //Start the mystery again ninjia.aoyi(); }
Data type: new
public static void main(String[] args) { //Mode 1 IntFunction<int []> arr1 = new IntFunction<int[]>() { @Override public int[] apply(int num) { return new int[num]; } }; arr1.apply(10); //Method 2 (method reference) IntFunction<int []> arr2 = int[]::new; arr2.apply(10); }
- In addition, there are many forms, which will not be repeated here
Development common
Common tips: traversing array printing
Comparing the three methods, we can see the degree of simplicity
ArrayList<Integer> arrayList=new ArrayList<>(); arrayList.add(1); arrayList.add(2); arrayList.add(3); //Anonymous Inner Class arrayList.forEach(new Consumer<Integer>() { @Override public void accept(Integer integer) { System.out.println(integer); } }); //lambda optimization arrayList.forEach((integer)-> System.out.println(integer)); //Method reference printing replaces our anonymous inner class with method reference (equivalent to replacing lambda) arrayList.forEach(System.out::println);
Traverse Map
Map<Integer, String> map = new HashMap<>(); map.forEach((k,v)->System.out.println(v));
Lambda expression scope
Accessing local common variables
- Only outer local variables marked final can be referenced
That is, local variables defined outside the domain cannot be modified inside lambda, otherwise compilation errors will occur.
- In special cases, local variables may not be declared final, but they must not be modified by the following code (i.e. implicit with final semantics)
Destroy implicit final (modify again)
package com.melo.notes.lambdaTest; public class TestFinal { interface MeiYan{ Integer change(String str); } public static void main(String[] args) { //Define local variables String temp = "222"; //You can write a line without return MeiYan meiYan = (str -> Integer.valueOf(str+temp)); //Modify again, which does not conform to the implicit final definition temp = "333"; Integer str =meiYan.change("111") ; System.out.println(str); } }
- It is not allowed to declare a parameter or local variable with the same name as a local variable.
Accessing local reference variables
import java.util.ArrayList; public class TestArray { interface MeiYan{ Integer change(); } void testArrayList(){ ArrayList<String> list = new ArrayList<>(); list.add("111"); //Accessing external reference local reference variables MeiYan meiYan = (() -> Integer.valueOf(list.get(0))); //Modify local reference variables list.set(0,"222"); Integer str =meiYan.change(); System.out.println(str); } public static void main(String[] args) { new TestArray().testArrayList(); } }
- There is no problem accessing reference variables. Because lambda can sense the external changes to the reference variable, there will be no data synchronization problem
For details, see the "understanding" below for a more detailed explanation
Accessing static and instance variables
All are OK, and there will be no error when you modify it again
code
public class TestFinal { //Static variable static String StaticTemp; //Instance variable String instanceTemp; interface MeiYan{ Integer change(String str); } void testStatic(){ StaticTemp="222"; MeiYan meiYan = (str -> Integer.valueOf(str+StaticTemp)); StaticTemp="333"; Integer str =meiYan.change("111") ; System.out.println(str); } public static void main(String[] args) { new TestFinal().testStatic(); }
understand
The difference between instance variables and local variables
The key question here is: what is the difference between instance variables and local variables?
- Instance variables are stored on the heap
The heap is shared between threads.
- Local variables are stored on the stack
There may be a corresponding thread problem (see below)
Thread problem
For example, thread A allocates A variable temp
- It is possible that Lambda is used in another thread B. thread B using Lambda may access the temp variable after thread A allocating the variable retracts the temp variable.
Data synchronization problem
Think of our ordinary methods. The parameters of the method only exist in the space of the method stack, and {} of our lambda is actually equivalent to a method block.
- If the method block here accesses an external variable, and the variable is only a common data type, it is equivalent to accessing only a copy. When this variable is modified externally, the internal (only copy) of lambda cannot perceive the modification of this variable.
Therefore, in order to prevent the problem of data synchronization, Java 8 limits: when lambda accesses local ordinary data type variables, it needs to use final modification or implicit final method!
Extension -- value passing or reference passing?
- It's just mentioned here that there are restrictions on local variables of common data types, but what about local variables of reference types? This involves whether Java parameter value passing is value passing or reference passing.
Occupy a pit first, and update a blog about this in this column in the future!!!
Problem solving
There is a question at the beginning. It seems that the method reference omits the parameters. Who do we operate on Intger.parseInt?
- In fact, I confused lambda. When lambda was defined, the parameter was not an actual parameter at all
It can be said that the parameter only serves the method body, but it will be used in the method body
We all use method references. The premise is that the parameters are the same as the return value, and the method body is also the content we want to implement. At this time, we don't need to write the method body, and the parameters that the method body depends on naturally don't need to be used
Write at the end
- I'm going to start a busy project recently. A deeper grasp of these basic knowledge may be put on when preparing for the interview in the future. At present, I don't understand deeply. If there are mistakes, please point out!
This column will also update some basic Java knowledge and experience on and off, so as to lay a foundation for future interviews in advance!