Prior to Java 8, an anonymous class that implemented an interface with only one abstract method looked more like a Lambda expression. In the following code, the anonymousClass method calls the waitFor method as a Condition class that implements the interface to shut down the Server when certain conditions are met. The following code is a typical use of anonymous classes.

void anonymousClass(a) {
    final Server server = new HttpServer();
    waitFor(new Condition() {
        @Override
        public Boolean isSatisfied(a) {
            return !server.isRunning();
        }
    }
Copy the code

The following code uses Lambda expressions to do the same thing:

void closure(a) { 
     Server server = newHttpServer(); waitFor(() -> ! server.isRunning()); }Copy the code

In fact, the waitFor method above is more similar to the following code description:

class WaitFor {
    static void waitFor(Condition condition) throws   
    InterruptedException {
        while(! condition.isSatisfied()) Thread.sleep(250); }}Copy the code

Some theoretical differences In practice, both implementations of the above methods are closures, and the implementation of the latter is the Lambda representation. This means that both need to hold the runtime environment. Prior to Java 8, this required copying everything the anonymous class needed into it. In the example above, you need to copy the Server property to the anonymous class.

Because it is copied, variables must be declared as final to ensure that they will not be changed when retrieved and used. Java ensures that variables are not updated in an elegant way, so we don’t explicitly add final to variables.

Lambda expressions, on the other hand, do not need to copy variables into their runtime environment, so Lambda expressions are treated as a real method, not an instance of a class.

Lambda expressions do not need to be instantiated every time, which is a huge benefit to Java. Unlike instantiating anonymous classes, the memory impact can be minimized.

In general, there are some differences between anonymous methods and anonymous classes:

Classes must be instantiated, but methods do not; When a class is created, the object needs to be allocated memory; Method allocates memory only once, which is stored in a permanent part of the heap; An object operates on its own data, while a method does not; Methods in static classes function like anonymous methods.

Some specific differences There are some specific differences between anonymous methods and anonymous classes, including retrieving semantics and overriding variables.

Getting semantics The this keyword is one of those semantic differences. In anonymous classes, this refers to an instance of an anonymous class. With an InnerClass called Foo$InnerClass, for example, code like foo.this. x looks strange when you refer to the scope of the InnerClass closure. In Lambda expressions, this refers to the closure scope. In fact, a Lambda expression is a scope, which means you don’t need to inherit any name from the superclass or introduce any hierarchy of scopes. You can directly access properties, methods, and local variables in scope. For example, in the following code, a Lambda expression can directly access the firstName variable.

public class Example {
    private String firstName = "Tom";

    public void example(a) {
        Function<String, String> addSurname = surname -> {
            // equivalent to this.firstName
            return firstName + "" + surname;  // or even,   }; }}Copy the code

Here firstName is short for this.firstName. But in anonymous classes, you have to explicitly call firstName,

public class Example {
    private String firstName = "Jerry";

    public void anotherExample(a) {
        Function<String, String> addSurname = new Function<String,  
        String>() {
            @Override
            public String apply(String surname) {
                return Example.this.firstName + ""+ surname; }}; }}Copy the code

Override variables in Lambda expressions,

public class ShadowingExample {

    private String firstName = " Tim";

    public void shadowingExample(String firstName) {
        Function<String, String> addSurname = surname -> {
            return this.firstName + ""+ surname; }; }}Copy the code

Because this refers to a closed scope in a Lambda expression, this.firstName corresponds to the value “Tim”, not the value of the argument with its name. If this is removed, the method argument is referenced.

In the example above, if implemented as an anonymous class, firstName refers to the method parameter; If you want to access the outermost firstName, use example.this.firstname.

public class ShadowingExample {

    private String firstName = "King";

    public void anotherShadowingExample(String firstName) {
        Function<String, String> addSurname = new Function<String,  
        String>() {
            @Override
            public String apply(String surname) {
                return firstName + ""+ surname; }}; }}Copy the code

Basic syntax for Lambda Expressions Lambda expressions are basically blocks of anonymous functions. It’s more like an instance of an inner class. For example, if we want to sort an array, we could use the arrays.sort method, which takes the Comparator interface as an argument, similar to the following code.

Arrays.sort(numbers, new Comparator<Integer>() {
    @Override
    public int compare(Integer first, Integer second) {
        returnfirst.compareTo(second); }});Copy the code

The Comparator instance in the parameter is an abstract fragment. This is only used in the sort method. If we replace it with a new syntax, implement it as a Lambda expression:

Arrays.sort(numbers, (first, second) -> first.compareTo(second));
Copy the code

This is much cleaner, and Java actually treats it like an instance of the Comparator class. If we extract the second argument to sort from the Lambda expression, it is of type Comparator.

Comparator<Integer> ascending = (first, second) -> first.compareTo(second);
Arrays.sort(numbers, ascending);
Copy the code

Decomposition of grammar

You can convert a single abstract method into a Lambda expression. For Example, if we have an interface named Example that contains only one abstract method, apply, that returns a type.

interface Example {
     R apply(A args);
}
Copy the code

We can implement methods in this interface anonymously:

new Example() {
    @Override
    public R apply(A args) {
        body
    }
};
Copy the code

To convert to Lambda expressions, we strip out the instance and declaration, strip out the details of the method, and keep only the argument list and body of the method.

(args) {
    body
}
Copy the code

We introduce a new symbol (->) to represent Lambda expressions.

(args) -> {
    body
}
Copy the code

Take the previous sorting method as an example, first we use anonymous class implementation:

Arrays.sort(numbers, new Comparator<Integer>() {
    @Override
    public int compare(Integer first, Integer second) {
        returnfirst.compareTo(second); }});Copy the code

Next, remove instance and method signatures:

Arrays.sort(numbers, (Integer first, Integer second) {
    return first.compareTo(second);
});
Copy the code

Reference Lambda expressions:

Arrays.sort(numbers, (Integer first, Integer second) -> {
    return first.compareTo(second);
});
Copy the code

Done! But some areas can be further optimized. You can remove the type of the argument, and the compiler is smart enough to know the type of the argument.

Arrays.sort(numbers, (first, second) -> {
    return first.compareTo(second);
});
Copy the code

If it is a simple expression, such as a single line of code, you can remove the curly braces from the body of the method and the return keyword if it has a return value.

Arrays.sort(numbers, (first, second) -> first.compareTo(second));
Copy the code

If a Lambda has only one argument, the parentheses around the argument can also be removed.

(x) -> x + 1
Copy the code

So when I get rid of the parentheses,

x -> x + 1
Copy the code

So the next step is to summarize,

(int x, int y) -> { return x + y; }
(x, y) -> { return x + y; }
(x, y) -> x + y; x -> x * 2
() -> System.out.println("Hello");
System.out::println;
Copy the code

The first is the full way Lambda is declared and used, but it’s a bit redundant. In fact, the type of the argument can be omitted. The second approach is to remove Lambda expressions of argument types; Third, if your method body is a one-line statement, you can simply omit the braces and return keyword. The fourth approach is a Lambda expression with no arguments; The fifth method is a variant of Lambda expressions: a shorthand for Lambda expressions, called method references. Such as:

 System.out::println;
Copy the code

It is actually a shorthand for the following Lambda expression:

(value -> System.out.prinltn(value)
Copy the code

Delve into Lambda expressions

Functional interface

Java treats a Lambda expression as an instance of an interface type. It calls this form a functional interface. A functional interface is an interface that has a single method. Java calls this a “functional method,” but it is more commonly known as a single abstract method or SAM. For example, there are interfaces in the JDK such as Runnable and Callable.

@FunctionalInterface

Oracle introduced a new annotation called @functionalinterface that identifies an interface as a FunctionalInterface. It basically communicates this purpose, but the editor does some extra checking. For example, the following interface:

public interface FunctionalInterfaceExample {
    // compiles ok
}
Copy the code

The @functionalInterface annotation will compile an error:

@FunctionalInterface // <- error here
    public interface FunctionalInterfaceExample {
      // doesn't compile
}
Copy the code

The compiler will report an error with the detail “Invalid ‘@functionalInterface’ annotation; FunctionalInterfaceExample is not a functional interface “. This means that no single abstract method is defined. What if we define two abstract methods?

@FunctionalInterface
public interface FunctionalInterfaceExample {
    void apply(a);
    void illegal(a); // <- error here
}
Copy the code

Multiple, non-ancient abstract methods were found. Therefore, once this annotation is used, only one abstract method can be defined in the interface.

Now there is such a situation, such as a song interface inherited from another interface, what will happen? We create A new functional interface called A and define another interface B that inherits FROM A, so B is still A functional interface that inherits from A’s apply method.

@FunctionalInterface
interface A {
    abstract void apply(a);
}

interface B extends A {
Copy the code

If you want clarity, you can override the parent method:

@FunctionalInterface
interface A {
    abstract void apply(a);
}

interface B extends A {
    @Override
    abstract void apply(a);
}
Copy the code

We can use the following code to test whether the above two interfaces are functional:

@FunctionalInterface
public interface A {
    void apply(a);
}

public interface B extends A {
    @Override
    void apply(a);
}

public static void main(String... args) {
   A a = () -> System.out.println("A");
   B b = () -> System.out.println("B");
   a.apply(); // Print: A
   b.apply(); // Print: B
}
Copy the code

If interface B inherits interface A, then no new methods can be defined in interface B, otherwise the compiler will report an error.

In addition to this, there are some new interface improvements in Java 8:

You can add default methods; Can include static interface methods; New interfaces have been added to the java.util.function package, for example, function and Predicate.

Method references

Simply put, a method reference is a shorthand for a Lambda expression. When you create a Lambda expression, you create an anonymous method and provide the method body, but when you use a method reference, you only need to provide the name of an existing method that already contains the method body. Its basic syntax is as follows;

Class::method
Copy the code

Or a more succinct example:

String::valueOf
Copy the code

The “::” symbol is preceded by the target reference and followed by the name of the method. So, in the above example, the String class is the target class that looks for its method valueOf, which we refer to as the static method on the String class.

public static String valueOf(Object obj) {... }Copy the code

“::” is called a delimiter. When we use it, we only refer to the method we want to use, not call the method, so we cannot add () after the method.

String::valueOf(); // error
Copy the code

You can’t call a method reference directly, just instead of a Lambda expression, so you can use a method reference wherever a Lambda expression is used. Therefore, the following code does not run:

public static void main(String... args) {
    String::valueOf;
}
Copy the code

This is because the method reference cannot be converted to a Lambda expression because the compiler does not have the context to infer what type of Lambda to create. We know that this reference is actually equivalent to the following code:

(x) -> String.valueOf(x)
Copy the code

But the compiler doesn’t know that yet. Although it can know a few things. It knows that, as a Lambda, the return value should be a string because the valueOf method returns a string value. But it does not know what information it needs to provide as an argument. We need to give it a little help, give it more context. Let’s create a functional interface, Conversion,

@FunctionalInterface
interface Conversion {
    String convert(Integer number);
}
Copy the code

Next we need to create a scenario to use this interface as a Lambda, and we define the following methods:

public static String convert(Integer number, Conversion function) {
    return function.convert(number);
}
Copy the code

In fact, we’ve given the compiler enough information to convert a method reference into an equivalent Lambda. When we call the convert method, we can pass the following code to Lambda.

convert(100, (number) -> String.valueOf(number));
Copy the code

We can replace the above Lambda with a method reference,

convert(100, String::valueOf);
Copy the code

Alternatively, we can tell the compiler to assign a reference to a type:

Conversion b = (number) -> String.valueOf(number);
Copy the code

Represented by method references:

Conversion b = String::valueOf
Copy the code

The type of method reference

In Java, there are four types of method references:

Constructor method reference; Static method references: Two instance method references. The last two are a bit confusing. The first is a method reference for a specific object, and the second is a method reference for any object, but a method reference of a specific type. The difference is how you want to use the method, if you don’t know in advance whether there are instances.

Constructor reference

The basic reference to the constructor is as follows:

String::new
Copy the code

It creates a Lambda expression and calls the String no-argument constructor. It is actually equivalent to:

() - >new String()
Copy the code

Note that the constructor reference has no parentheses. It is a reference, not a call. The above example simply refers to the constructor of the String class, without actually instantiating a String object. Let’s look at an example of a constructor reference in action. Look at the previous example. Loop ten times to add objects to list.

public void usage(a) {
    List<Object> list = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
        list.add(newObject()); }}Copy the code

If we want to reuse the instantiation functionality, we can extract a new method initialise to create an object with a factory.

public void usage(a) {
    List<Object> list = newArrayList<>(); initialise(list, ...) ; }private void initialise(List<Object> list, Factory<Object> factory){
    for (int i = 0; i < 10; i++) { list.add(factory.create()); }}Copy the code

Factory is a functional interface that contains a create method that returns an Object, which we can add to the list as a Lambda.

public void usage(a) {
    List<Object> list = new ArrayList<>();
    initialise(list, () -> new Object());
}
Copy the code

Or we could replace it with a constructor reference:

public void usage(a) {
    List<Object> list = new ArrayList<>();
    initialise(list, Object::new);
}
Copy the code

The above method needs to be improved. The above method only creates objects of type Object. We can add generics to implement methods that can create more types.

public void usage(a) {
    List<String> list = new ArrayList<>();
    initialise(list, String::new);
}

private <T> void initialise(List<T> list, Factory<T> factory) {
    for (int i = 0; i < 10; i++) { list.add(factory.create()); }}Copy the code

As far as we know, we are demonstrating references to a constructor that has no arguments. What if a constructor reference takes an argument? When there are multiple constructors, the same syntax is used, but the compiler calculates which constructor is the best match. It is based on the target type and the inference function interface, which can be used to create that type. For example, we have a Person class that has a constructor for multiple arguments.

class Person {
    public Person(String forename, String surname, LocalDate    
    birthday, Sex gender, String emailAddress, int age) {
      // ...
    }
Copy the code

Returning to the above example, we could use:

initialise(people, () -> new Person(forename, surname, birthday,
                                    gender, email, age));
Copy the code

But if you want to use this constructor reference, you need the Lambda expression to provide the following arguments:

initialise(people, () -> new Person(forename, surname, birthday,
                                    gender, email, age));
Copy the code

The following is an example of a method reference for a specific object:

x::toString
Copy the code

X is the object we want to get. It is equivalent to the Lambda expression below.

() -> x.toString()
Copy the code

This method reference provides a convenient way to switch between different functional interface types. See the examples:

Callable<String> c = () -> "Hello";
Copy the code

The Callable method is call and returns “Hello” when called. If we have another functional interface Factory, we can use method references to transform the Callable functional interface.

Factory<String> f = c::call;
Copy the code

We can recreate a Lambda expression, but this technique is a useful way to reuse pre-defined lambdas. Assign them to variables and reuse them to avoid duplication. We have the following example:

public void example(a) {
    String x = "hello";
    function(x::toString);
}
Copy the code

This example uses closures for method references. He creates a Lambda to call the toString method on the x object. The signature and implementation of the function method above are as follows:

public static String function(Supplier<String> supplier) {
    return supplier.get();
}
Copy the code

The functional interface Supplier is defined as follows:

@FunctionalInterface
public interface Supplier<T> {
  T get(a);
}
Copy the code

When this method is used, it returns a string through the get method, and that is the only way to get a string in our structure. It is equivalent to:

public void example(a) {
  String x = "";
  function(() -> x.toString());
}
Copy the code

Note that the Lambda expression here has no arguments. This indicates that the x variable is not available in the local scope of the Lambda and must be placed outside its scope if it is available. We have to cover up the variable x. If you were to implement it as an anonymous class, it would look something like this, which requires some idea of how the x variable is passed.

public void example(a) {
    String x = "";
    function(new Supplier<String>() {
        @Override
        public String get(a) {
            return x.toString(); // <- closes over 'x'}}); }Copy the code

Instance method references for any object (provided later by instance) The last type of instance method reference looks like this:

Object::toString
Copy the code

Although the pointer to the left of the “::” refers to a class (sort of like a static method reference), it actually refers to an Object. The toString method is an instance method on the Object class, not a static method. The reason you might not use the regular instance method syntax is that there is no instance referenced yet. In the past, when we called x::toString, we knew the type of x, but in some cases we didn’t. You could still pass a method reference, but you would need to provide the corresponding type to use this syntax later. For example, the following expression is equivalent to an unbounded type of X.

(x) -> x.toString()
Copy the code

There are two different examples of methods that are referenced mostly academically. Sometimes, you need to pass something along, and other times, the use of Lambda will provide it for you. This example is similar to a regular method reference; This time it calls the toString method of String, which is supplied to functions that use Lambda, not functions passed from an external scope.

public void lambdaExample(a) {
    function("value", String::toString);
}
Copy the code

This String looks like a reference to a class, but is actually an instance. If this is confusing, for clarity we need to look at a method that uses Lambda expressions as follows:

public static String function(String value, Function<String, String> function) {
    return function.apply(value);
}
Copy the code

So, the String instance is passed directly to the method, which looks like a fully qualified Lambda.

public void lambdaExample(a) {
    function("value", x -> x.toString());
}
Copy the code

The above code can be shortened toString ::toString, which says give me an object instance at run time. If you want to use anonymous class expansion, it looks like this. The argument x is available and not shaded, so it is more like a Lambda expression than a closure.

public void lambdaExample(a) {
    function("value".new Function<String, String>() {
      @Override
      // takes the argument as a parameter, doesn't need to close 
      over it
      public String apply(String x) {
        returnx.toString(); }}); }Copy the code

Summary of method references

Oracle describes four types of method references, as shown below.

species For example,
Static method reference ContainingClass::staticMethodName
An instance method reference for a particular object ContainingObject::instanceMethodName
An instance method reference to any object of a particular type ContainingType::methodName
Constructor reference ClassName::new

Here is the syntax and examples of method references.

species grammar For example,
Static method reference Class::staticMethodName String::valueOf
An instance method reference for a particular object object::instanceMethodName x::toString
An instance method reference to any object of a particular type Class::instanceMethodName String::toString
Constructor reference ClassName::new String::new

Finally, the method reference above is equivalent to the corresponding Lambda expression below.

species grammar Lambda
Static method reference Class::staticMethodName (s) -> String.valueOf(s)
An instance method reference for a particular object object::instanceMethodName () -> “hello”.toString()
An instance method reference to any object of a particular type Class::instanceMethodName (s) -> s.toString()
Constructor reference ClassName::new () -> new String()







Fan Rabbit Education is a pan-Internet vocational education platform, the official website address:ftuedu.com/