英文:
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 表达式。
现在,你的 animal
是 Animal
接口的一个实例,并被分配了一个方法引用,稍后可以调用它。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
方法的方式完全相同。只不过你的接口叫做 Animal
和 eat
,而不是 Runnable
和 run
。
因此,按照同样的逻辑,使用 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"。
或者,你还可以创建一个方法引用来引用新创建的 Dog
的 eat
方法,以输出 "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;
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论