The odd trick of using lambda expressions in Java programming

  • 2020-05-07 19:34:59
  • OfStack

Why does use Lambda expressions
:

The first example is to execute a task in a separate thread.


class Worker implements Runnable {
  public void run() {
    for (int i = 0; i < 100; i++)
      doWork();
  }
  ...
}

Worker w = new Worker();
new Thread(w).start();

Second example, custom string comparison method (by string length), 1 do this:


class LengthComparator implements Comparator<String> {
  public int compare(String first, String second) {
    return Integer.compare(first.length(), second.length());
  }
}
Arrays.sort(strings, new LengthComparator());

Third example, in JavaFX, add 1 callback to 1 button:


button.setOnAction(new EventHandler<ActionEvent>() {
  public void handle(ActionEvent event) {
    System.out.println("Thanks for clicking!");
  }
});

One thing these examples have in common is that they define a block of code, pass it to an object or method, and then execute it. In Lambda table
Until then, Java is not allowed to pass blocks of code directly, because Java is object-oriented, so it must pass 1 object
Executed blocks of code are encapsulated in an object.

Syntax for Lambda expressions
represents LengthComparator in the second example above by the expression Lambda:


(String first, String second) -> Integer.compare(first.length(), 
  second.length());

- > Before is the parameter list, followed by the expression statement body;

If the body of the expression statement is more than 1 line, the body of the statement is written in {}, just like ordinary function 1:


(String first, String second) -> {
  if (first.length() > second.length()) {
    return 1;
  } else if (first.length() == second.length()) {
    return 0;
  } else {
    return -1;
  }
};

If there is no parameter, () still needs to be taken, for example, the first example above can be expressed as:


() -> {
  for (int i = 0; i < 1000; i ++) {
    doWork();
  }
}

If the type of the parameter can be inferred automatically from the context, omit:


Comparator<String> comp
  = (first, second) // Same as (String first, String second)
  -> Integer.compare(first.length(), second.length());

Braces () can also be omitted if there is only one argument and the type can be automatically inferred:


// Instead of (event) -> or (ActionEvent event) ->
eventHandler<ActionEvent> listener = 
  event -> System.out.println("Thanks for clicking!");

The type of the return value of the lambda expression is automatically inferred, so there is no need to specify; In the lambda expression, there are some conditional branches
Return value, while other branches do not return value, is not allowed, such as:


(x) -> {
  if (x >= 0) {
    return 1;
  }
}

Also, the difference between expression lambda and statement lambda is that expression lambda is not required
Write return keyword, Java runtime returns the result of the expression as the return value, statement lambda is
Expressions written in {} require the use of the return keyword, such as:


// expression lambda
Comparator<String> comp1 = 
  (first, second) -> Integer.compare(first.length(), second.length());

// statement lambda
Comparator<String> comp2 = (first, second) -> 
  { return Integer.compare(first.length(), second.length());};

Functional Interface
is called if one interface (interface) has only one abstract method (abstract method)
Functional Interface, e.g. Runnable, Comparator, etc.
The lambda expression can be used anywhere an Functional Interface object is needed:


class LengthComparator implements Comparator<String> {
  public int compare(String first, String second) {
    return Integer.compare(first.length(), second.length());
  }
}
Arrays.sort(strings, new LengthComparator());
0

Here, the second parameter of sort() requires an Comparator object, and Comparator is
Functional Interface, so you can directly pass in the lambda expression before calling the compare() method of the object
Is the body of the statement in the lambda expression.

If the statement of an lambda expression throws an exception, the abstract method in the corresponding Functional Interface must be thrown
Exception, otherwise you need to explicitly catch the exception in the lambda expression:


Runnable r = () -> {
  System.out.println("------");
  try {
    Thread.sleep(10);
  } catch (InterruptedException e) {
    // catch exception
  }
};

Callable<String> c = () -> {
  System.out.println("--------");
  Thread.sleep(10);
  return "";
};

Method Reference
lambda expression if the parameters of lambda expression are passed to a method as parameters, and their execution effect is the same, then the lambda expression
It can be expressed by Method Reference. The following two ways are equivalent:


(x) -> System.out.println(x)
System.out::println

System.out::println is called Method Reference.

Method Reference mainly has three forms:

object::instanceMethod Class::staticMethod Class::instanceMethod

For the first two methods, the parameters of the corresponding lambda expression and method are 1, for example:


class LengthComparator implements Comparator<String> {
  public int compare(String first, String second) {
    return Integer.compare(first.length(), second.length());
  }
}
Arrays.sort(strings, new LengthComparator());
3

For the third way, the corresponding lambda expression in the statement body, the first parameter as an object, call method, other parameters
As a parameter of method, for example:


class LengthComparator implements Comparator<String> {
  public int compare(String first, String second) {
    return Integer.compare(first.length(), second.length());
  }
}
Arrays.sort(strings, new LengthComparator());
4

Constructor Reference is similar to Method Reference except that it is a special method: new, depending on which constructor is called, for example:


class LengthComparator implements Comparator<String> {
  public int compare(String first, String second) {
    return Integer.compare(first.length(), second.length());
  }
}
Arrays.sort(strings, new LengthComparator());
5

Button::new is equivalent to (x) - > Button(x), so the constructor is: Button(x);

In addition to creating a single object, you can also create an array of objects, as shown below:


class LengthComparator implements Comparator<String> {
  public int compare(String first, String second) {
    return Integer.compare(first.length(), second.length());
  }
}
Arrays.sort(strings, new LengthComparator());
6

variable scope
The
lambd expression captures the variables available in the current scope, such as:


public void repeatMessage(String text, int count) {
  Runnable r = () -> {
    for (int i = 0; i < count; i ++) {
      System.out.println(text);
      Thread.yield();
    }
  };
  new Thread(r).start();
}

But these variables have to be immutable. Why? Here's an example:


int matches = 0;
for (Path p : files)
  new Thread(() -> { if (p has some property) matches++; }).start(); 
  // Illegal to mutate matches

Because mutable variables are not thread-safe in lambda expressions, they are 1 to the inner class, which can only be referenced
Externally defined final variable;

The scope of the lambda expression is 1 like the scope of the nested code block, so the parameter or variable names in the lambd expression are not
Can conflict with local variables, such as:


class LengthComparator implements Comparator<String> {
  public int compare(String first, String second) {
    return Integer.compare(first.length(), second.length());
  }
}
Arrays.sort(strings, new LengthComparator());
9

If the this variable is referenced in an lambda expression, the this variable of the method that created the lambda expression is referenced, such as:


public class Application() {
  public void doWork() {
    Runnable runner = () -> {
      ...;
      System.out.println(this.toString());
      ...
    };
  }
}

So this.toString () calls toString() of the Application object, not Runnable
Of the object.


Default Method
You can only have abstract methods in the
interface, and if you add a method to an existing interface, all the implementation classes of that interface need to implement that method.
Java 8 introduces the concept of Default Method, adding an default method to the interface without breaking the existing connection
Interface rules, interface implementation classes can choose to override or directly inherit the default method, such as:


interface Person {
  long getId();
  default String getName() { return "John Q. Public"; }
}

Java allows multiple inheritance if the method defined in the parent of a class is exactly the same as the default method defined in the interface, or
Exactly the same method is defined in two interfaces of a class. How do you handle this conflict? The processing rules are as follows:

If there is a method conflict between the parent and the interface: the method in the parent class prevails and the method in the interface is ignored.
If the default method conflicts between the two interfaces, the method needs to be overridden to resolve the conflict.

Static Method
Before
Java 8, only static variables could be defined in the interface. Starting with Java 8, static methods could be added to the interface, for example
Comparator interface adds 1 series comparingXXX static methods, such as:


public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> 
  keyExtractor) {
  Objects.requireNonNull(keyExtractor);
  return (Comparator<T> & Serializable)
   (c1, c2) -> Integer.compare(keyExtractor.applyAsInt(c1), 
       keyExtractor.applyAsInt(c2));
}

Using the static method, the following two methods are also equivalent:

1.


Arrays.sort(cities, (first, second) -> Integer.compare(first.length(), 
  second.length()));

2,


Arrays.sort(cities, Comparator.comparingInt(String::length));

Therefore, we do not need to define separate utility classes (such as Collections/Collection) when we design our own interfaces in the future.
Just use the static method in the interface.

anonymous inner class

In the Java world, anonymous inner classes can implement operations that may only be performed once in an application. For example, in the Android application, the click event of a button is handled. Instead of writing a separate class to handle a single click, you can use an anonymous inner class to do this:


Button button = (Button) findViewById(R.id.button1);
button.setOnClickListener(new OnClickListener() {
 
  @Override
  public void onClick(View view) {
    Toast.makeText(MainActivity.this, "Button Clicked", Toast.LENGTH_SHORT).show();
  }
 
});
Lambda example 1.Runnable Lambda Take a look at some examples. Here is an example of Runnable:

public void runnableTest() {
    System.out.println("=== RunnableTest ===");
    // 1 An anonymous  Runnable
    Runnable r1 = new Runnable() {
      @Override
      public void run() {
        System.out.println("Hello world one!");
      }
    };
    // Lambda Runnable
    Runnable r2 = () -> System.out.println("Hello world two!");
    //  Execute two  run  function 
    r1.run();
    r2.run();
  }

  public void runnableTest() {
    System.out.println("=== RunnableTest ===");
    // 1 An anonymous  Runnable
    Runnable r1 = new Runnable() {
      @Override
      public void run() {
        System.out.println("Hello world one!");
      }
    };
 
    // Lambda Runnable
    Runnable r2 = () -> System.out.println("Hello world two!");
 
    //  Execute two  run  function 
    r1.run();
    r2.run();
  }
Neither implementation takes any arguments or returns any values. The Runnable lambda expression simplifies five lines of code to one statement in the form of a code block.
2 . Comparator Lambda In Java, the Comparator interface is used to sort collections. In the following example, an ArrayList contains 1 Person objects, sorted by surName of Person objects. Here are the fields contained in the Person class:

public class Person {
  private String givenName;
  private String surName;
  private int age;
  private Gender gender;
  private String eMail;
  private String phone;
  private String address;
}

public class Person {
  private String givenName;
  private String surName;
  private int age;
  private Gender gender;
  private String eMail;
  private String phone;
  private String address;
}
Here's how to implement the Comparator interface using anonymous inner classes and Lambda expressions, respectively:

public class ComparatorTest {
  public static void main(String[] args) {
    List<Person> personList = Person.createShortList();
    //  Sort using inner classes 
    Collections.sort(personList, new Comparator<Person>() {
      public int compare(Person p1, Person p2) {
        return p1.getSurName().compareTo(p2.getSurName());
      }
    });
    System.out.println("=== Sorted Asc SurName ===");
    for (Person p : personList) {
      p.printName();
    }
    //  use  Lambda  Expression implementation 
    //  Ascending order 
    System.out.println("=== Sorted Asc SurName ===");
    Collections.sort(personList, (Person p1, Person p2) -> p1.getSurName().compareTo(p2.getSurName()));
    for (Person p : personList) {
      p.printName();
    }
    //  Descending order 
    System.out.println("=== Sorted Desc SurName ===");
    Collections.sort(personList, (p1, p2) -> p2.getSurName().compareTo(p1.getSurName()));
    for (Person p : personList) {
      p.printName();
    }
  }
}

public class ComparatorTest {
  public static void main(String[] args) {
    List<Person> personList = Person.createShortList();
 
    //  Sort using inner classes 
    Collections.sort(personList, new Comparator<Person>() {
      public int compare(Person p1, Person p2) {
        return p1.getSurName().compareTo(p2.getSurName());
      }
    });
 
    System.out.println("=== Sorted Asc SurName ===");
    for (Person p : personList) {
      p.printName();
    }
 
    //  use  Lambda  Expression implementation 
 
    //  Ascending order 
    System.out.println("=== Sorted Asc SurName ===");
    Collections.sort(personList, (Person p1, Person p2) -> p1.getSurName().compareTo(p2.getSurName()));
    for (Person p : personList) {
      p.printName();
    }
 
    //  Descending order 
    System.out.println("=== Sorted Desc SurName ===");
    Collections.sort(personList, (p1, p2) -> p2.getSurName().compareTo(p1.getSurName()));
    for (Person p : personList) {
      p.printName();
    }
  }
}
You can see that anonymous inner classes can be implemented through the Lambda expression. Note that the first Lambda expression defines the type of the parameter to be Person; The second Lambda expression omits the type definition. The Lambda expression supports type overrides and omits the type definition if the desired type can be overridden from the context. Since we used the Lambda expression in an Comparator using a generic definition, the compiler can override the two arguments to Person.


Related articles: