Lambda - people love and hate "->“

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!

Posted on Wed, 03 Nov 2021 20:30:51 -0400 by gkostenarov