我只是在学习Java,而不是一名实践的程序员。
我要遵循的书说,重写方法时,参数类型必须相同,但是返回类型可以是多态兼容的。
我的问题是为什么传递给重写方法的参数不能不是预期的超类型的子类类型?
在重载方法中,可以保证在对象上定义了我对对象调用的任何方法。
有关建议的重复项的注意事项:
第一个建议似乎是关于类层次结构以及在何处放置功能。我的问题更集中于为什么存在语言限制。
在第二个建议,说明如何做我问什么,而不是为什么它做的方式。我的问题集中在为什么。
我只是在学习Java,而不是一名实践的程序员。
我要遵循的书说,重写方法时,参数类型必须相同,但是返回类型可以是多态兼容的。
我的问题是为什么传递给重写方法的参数不能不是预期的超类型的子类类型?
在重载方法中,可以保证在对象上定义了我对对象调用的任何方法。
有关建议的重复项的注意事项:
第一个建议似乎是关于类层次结构以及在何处放置功能。我的问题更集中于为什么存在语言限制。
在第二个建议,说明如何做我问什么,而不是为什么它做的方式。我的问题集中在为什么。
Answers:
您最初在问题中提到的概念称为协变返回类型。
协变返回类型之所以起作用,是因为方法应该返回某种类型的对象,而重写方法实际上可能返回该对象的子类。根据Java之类的语言的子类型化规则,如果S
是的子类型T
,那么无论T
出现在哪里,我们都可以传递S
。
因此,S
在覆盖预期a的方法时,返回a 是安全的T
。
您建议接受覆盖方法使用作为覆盖方法所请求参数的子类型的参数的建议要复杂得多,因为这会导致类型系统不健全。
一方面,通过上述相同的子类型化规则,很可能它已经可以满足您的需求。例如
interface Hunter {
public void hunt(Animal animal);
}
没有什么可以阻止此类的实现接收任何种类的动物的,因为它已经满足了您提出的问题的标准。
但让我们假设我们可以按照您的建议覆盖此方法:
class MammutHunter implements Hunter {
@Override
public void hunt(Mammut animal) {
}
}
这是有趣的部分,现在您可以执行以下操作:
AnimalHunter hunter = new MammutHunter();
hunter.hunt(new Bear()); //Uh oh
根据AnimalHunter
您的公共接口,您应该可以猎杀任何动物,但是根据您的实现,MammutHunter
您只能接受Mammut
对象。因此,重写的方法不满足公共接口。我们只是在这里破坏了类型系统的健全性。
您可以使用泛型来实现所需的功能。
interface AnimalHunter<T extends Animal> {
void hunt(T animal);
}
然后,您可以定义您的MammutHunter
class MammutHunter implements AnimalHunter<Mammut> {
void hunt(Mammut m){
}
}
使用通用协方差和逆方差,您可以在必要时放宽对自己有利的规则。例如,我们可以确保哺乳动物猎人只能在给定的背景下猎取猫科动物:
AnimalHunter<? super Feline> hunter = new MammalHunter();
hunter.hunt(new Lion());
hunter.hunt(new Puma());
假设MammalHunter
工具AnimalHunter<Mammal>
。
在这种情况下,这将不被接受:
hunter.hunt(new Mammut()):
即使当哺乳动物是哺乳动物时,由于我们在此使用的逆变类型的限制,它也不被接受。因此,您仍然可以对类型进行某些控制,以执行您提到的操作。