简体   繁体   中英

How does lambdas and reference method work in this example?

I have a doubt about the functioning of the following code since the Car class does not implement the Rideable interface, and I don't know how it is possible?

I'm using two ways to do it, one is lambda and the other with reference method.

interface Rideable {
    Car getCar(String name);
}

class Car {
    private String name;

    public Car(String name) {
        this.name = name;
    }
    
    public String getName() {
        return name;
    }

    public static void main(String[] args) {
        //both lines are valid for compilation
        Rideable rider = Car::new; 
        //Rideable rider = c -> new Car(c); 

        Car vehicle = rider.getCar("My Car");
        System.out.println(vehicle.getName());
    }
}

Car::new is not 'a new car'.

new Cake() is like: "make a cake; once you've made it, make that the value of the expression new Cake() .

Cake::new is nothing like a cake. It's not a cake whatsoever, and its value implements none of the interfaces cake do. After all, Cake::new is a cake recipe . It is a thing capable of making cakes , and that has nothing to do with cakes. You can eat a cake. You can't eat a recipe, or an oven. Or a baker (well, I guess you can, but let's not get weird with this. Certainly eating a baker is going to taste nothing like a cake).

In java, 'recipes' like Cake::new are 'untyped' - instead, the compiler looks at the context; the stuff around this expression. That's why this:

    Object o = Cake::new;

is not going to compile: From context, there's no hints whatsoever as to what you're trying to do here.

The context needs to tell the compiler exactly what sort of recipe is required, and then the compiler will check if the recipe you've written will fit these needs. If yeah, great, then that's what you get. If not, you get a compiler error.

So, back to your code:

a Rideable describes a recipe: Any recipe that takes as input a name, and turns that into a Car , can be deemed a 'Rideable'. Given that this is an interface that defines precisely one method, it is a so-called FunctionalInterface, and those are special: You can use lambda syntax or method reference syntax to create a new rideable on the fly. The compiler will 'fit' the lambda/reference expression to act as a new instance of Rideable.

Turns out, Car::new fits the bill, There is a constructor that takes a string, and constructors effectively 'return' a Car. which matches that one defined method that Rideable has, Thus, the compiler has determined from the context what you want, that it fits. and goes ahead and does it, Therefore: this:

Rideable r = Car::new;

is just syntax sugar for this:

Rideable r = name -> new Car(name);

which is basically syntax sugar* for this:

Rideable r = new Rideable() {
    public Car getCar(String name) {
        return new Car(name);
    }
}

which is syntax sugar for this:

    public class YourCustomRideable implements Rideable {
        @Override public Car getCar(String name) {
            return new Car(name);
        }
    }

    Rideable r = new YourCustomRideable();

*) There are slight, basically irrelevant differences between the two; the compiler produces slightly different byte code. There are ways to observe this difference, such as asking for the identity hashcode of the rider, but, don't. You're not supposed to treat these things as having a relevant identity, the whole point of your 'instance' of Rideable is that you intend to invoke getCar on it and nothing else . As long as you do that, these 4 lines of code are all entirely equivalent.

Car::new

has a meaning which depends on the context in which it is used.

In this case, it is the same as:

Rideable rider = name -> new Car(name);

A Rideable is just something which takes a String and gives you back a Car . This doesn't require Car to implement Rideable : you're using its constructor, which takes a String , and gives you back a Car .

That lambda is equivalent to new CarRideable() if there were a class defined somewhere like this:

class CarRideable implements Rideable {
  public Car getCar(String name) {
    return new Car(name);
  }
}

Car::new just says to use the Car constructor to implement the one and only method in Rideable , which it can.

Java 8 used the concept of interface for introducing another concept which is called lambda expression .

First of all you should know about FunctionalInterface , Functional interface is an interface with only one abstract method in it here you have defined Ridable interface which has only getCar method in it.

Second you should know lambda can be assigned to functional interface and the type of the lambda will be specified by the abstract method of the functional interface. So here instead of your code we could have:

Rideable rider = (String name) -> new Car(name);

here (String name) -> new Car(name) is a lamba expression, you can see the signature of lambda is the same as abstract method in Ridable Functional interface . But here the right hand side of lambda do nothing but calling a method (which here is a cunstroctor) so isn't it better to just refer to that method instead of creating a lambda like this:

Rideable rider = Car::new;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM