为什么它不输出 ‘dog eat’

huangapple go评论60阅读模式
英文:

Why doesn't it output 'dog eat'

问题

以下是您要翻译的代码部分:

public class LambdaTest {
    public static void main(String[] args) {
        final Animal animal = Dog::new;
        animal.eat();
    }
}

@FunctionalInterface
interface Animal {
    void eat();
}

class Dog implements Animal {

    public Dog() {
        System.out.println("dog init.");
    }

    @Override
    public void eat() {
        System.out.println("dog eat");
    }
}

注意:我只提供代码的翻译部分,不包括其他内容。

英文:

code like this

public class LambdaTest {
    public static void main(String[] args) {
        final Animal animal = Dog::new;
        animal.eat();
    }
}

@FunctionalInterface
interface Animal {
    void eat();
}

class Dog implements Animal {

    public Dog() {
        System.out.println("dog init.");
    }

    @Override
    public void eat() {
        System.out.println("dog eat");
    }

When I ran this code, "dog init." was printed to the console, but "dog eat" was not. Why is that? Can someone tell me the reason?

I expected "dog init" and "dog eat" to be printed, but only "dog init" was printed. Additionally, I'm puzzled as to why there was no error when Animal animal = Dog::new;.

答案1

得分: 6

Animal 是一个具有单个方法 void eat()(无返回值,无参数)的函数接口,也被称为 SAM 类型(单一抽象方法)。它在结构上与 java.util.Runnable 接口相同(interface Runnable { void run(); })。你的代码也可以这样写:

final Runnable runnable = Dog::new;
runnable.run(); // 调用构造函数

你可以将任何零参数且无返回值的 lambda 表达式或方法引用分配给它,例如 () -> {}

Dog::new 是一个方法引用,等效于 lambda 表达式 () -> { new Dog(); }。如果你仔细观察,你会注意到这个 lambda 表达式不返回任何内容。它创建一个新的 Dog 实例,然后立即忘记它。

将方法引用或 lambda 表达式分配给变量不会立即执行它。你必须显式调用接口中的命名方法来执行 lambda 表达式。

现在,你的 animalAnimal 接口的一个实例,并被分配了一个方法引用,稍后可以调用它。animal.eat() 调用分配的方法引用,从而调用构造函数。

如果你希望你的变量 animal 持有一个 Dog 实例,然后调用其 eat 方法,可以直接调用构造函数:Animal animal = new Dog();

"问题" 在于 eat 方法的签名,因为它等同于 Runnable#run,允许分配任何 "空" 动作。

在 Java 8 中引入了 lambda 表达式和方法引用。在 Java 8 之前的版本中,你必须创建匿名类来实现相同的行为:

final Animal animal = new Animal() {
  @Override public void eat() {
    new Dog();
  }
};
animal.eat(); // 调用匿名实例的 "eat" 方法,随后调用构造函数;不调用 `Dog#eat`
英文:

Animal is a functional interface with a single method void eat() (void return, no parameters) – also known as SAM type (single abstract method).

It is structurally identical to the java.util.Runnable interface (interface Runnable { void run(); }). Your code could as well be written as:

final Runnable runnable = Dog::new;
runnable.run(); // calls the constructor

You can assign it any lambda or method reference with zero arguments and no return value, e.g. () -> {}.

Dog::new is a method reference and equivalent to the lambda () -> { new Dog(); }. If you look closely, you will notice that this lambda does not return anything. It constructs a new Dog instance and then forgets about it again.

Assigning a method reference or a lambda to a variable does not execute it (yet). You have to explicitly invoke the lambda by calling the named method from your interface.

Now, your animal is an instance of your Animal interface and got assigned a method reference which can then be invoked later. animal.eat() invokes the assigned method reference, calling the constructor.

If you want your variable animal to hold a Dog instance and then invoke the eat method on it, call the constructor directly: Animal animal = new Dog();

The "problem" is the signature of your eat method, because it is equivalent to Runnable#run and allows assigning any "empty" action.

Lambdas and method references were introduced with Java 8. In Java versions before, you had to create an anonymous class to achieve the same behavior:

final Animal animal = new Animal() {
  @Override public void eat() {
    new Dog();
  }
};
animal.eat(); // calls "eat" of the anonymous instance, which in turn calls the constructor; does NOT call `Dog#eat`

答案2

得分: 2

Animal 是一个具有单个抽象方法 eat 的接口。这个方法不接受任何参数,也不返回任何内容。因此,它可以表示任何不接受参数的函数。例如:

Animal foo = () -> System.out.println("foo");

现在,如果我调用 foo.eat(),它将运行 lambda 中的代码,打印出 "foo"。

我还可以使用方法引用而不是 lambda:

Animal foo = SomeClass::someStaticMethod;

现在 foo.eat() 将运行位于 SomeClass 中的无参数方法 someStaticMethod

请注意,这与使用内置的 Runnable 接口并调用其 run 方法的方式完全相同。只不过你的接口叫做 Animaleat,而不是 Runnablerun

因此,按照同样的逻辑,使用 Dog 的构造函数引用将调用 Dog 的构造函数:

Animal foo = Dog::new;
Runnable bar = Dog::new;

// 这两行做了相同的事情:
foo.eat();
bar.run();

如果你使用 Runnable 写这段代码,你还会感到困惑吗?

请注意,Dog 实现 Animal 在这里并不重要。Dog 实现 Animal 只意味着你可以执行以下操作:

Animal animal = new Dog();

这具有完全不同的含义。如果此时我执行 animal.eat(),那么它将输出你所期望的 "dog eat"。

或者,你还可以创建一个方法引用来引用新创建的 Dogeat 方法,以输出 "dog eat":

Animal animal = new Dog()::eat;
英文:

Animal is an interface with a single abstract method, eat. This method does not take any parameters and does not return anything. Because of this, it can represent any function that takes no parameters. For example:

Animal foo = () -> System.out.println("foo");

Now if I call foo.eat(), it will run the code in the lambda, which prints "foo".

I can also use a method reference instead of a lambda:

Animal foo = SomeClass::someStaticMethod;

Now foo.eat() will run the parameterless method called someStaticMethod located in SomeClass.

Notice that this works in exactly the same way as using the built-in Runnable interface and called its run method. Yours is just called Animal and eat, rather than Runnable and run.

So by the same logic, using a constructor reference of Dog will invoke the constructor of Dog.

Animal foo = Dog::new;
Runnable bar = Dog::new;

// these two lines do the same thing:
foo.eat();
bar.run();

Would you still have the confusion if you have written the code with Runnable instead?

Note that the fact that Dog implements Animal does not matter here. Dog implements Animal just means that you can do:

Animal animal = new Dog();

which has a completely different meaning. If at this point I do animal.eat(), then it will output what you expect - "dog eat".

Alternatively, you could also make a method reference to a newly-created Dog's eat method for it to output "dog eat":

Animal animal = new Dog()::eat;

huangapple
  • 本文由 发表于 2023年2月16日 17:30:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/75470235.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定