我对理解多态实际如何工作有点困惑。至多,我理解超类引用变量可以引用子类对象引用,但我不能使用该引用变量来访问任何子类字段和方法。以此示例程序为例:
public class Game
{
public static void main(String[] args)
{
Human hero = new Knight();
hero.stats();
}
}
class Human
{
public void stats()
{
System.out.println("HP: 10 STR: 25.");
}
}
class Knight extends Human
{
public void skill()
{
System.out.println("Sword master");
}
public void weapon()
{
System.out.println("Sword");
}
}
从上面的示例程序中,我可以使用引用变量hero调用的唯一方法是在Human类中声明的方法,但是我无法访问Knight类中声明的任何方法。
如果我要在Human和Knight类中包含以下whoAmI()方法,则引用变量将引用子类overriden方法:
{...
hero.whoAmI() // calls the method declared in the Knight class
}
class Human
{
public void whoAmI()
{
System.out.println("Just a regular human");
}
}
class Knight extends Human
{
public void whoAmI()
{
System.out.println("A mighty knight");
}
}
虽然我确实理解为什么调用覆盖方法whoAmI()而不是超类'方法,因为JVM(如果我错了,请纠正我)看到引用变量包含对骑士对象的引用,为什么我不能使用引用变量访问Knight类中声明的任何方法?
答案 0 :(得分:1)
Human hero = new Knight();
您将自己的英雄定义为班级Human
。因此,您只会继承其父类可用的属性。
如果您将自己的英雄定义为Knight
,那么您将有权访问Human
和Knight
个属性。
Knight hero = new Knight();
一些挑剔:更好地声明你的属性private
,并使用getter / setter。
希望这有帮助。
答案 1 :(得分:1)
以这种方式思考
骑士英雄=新人类(); 为什么这是错的?
因为可访问/可见方法取决于类型的引用变量英雄。 因此,所有子方法都可以被称为逻辑,但从技术上讲,你没有持有子类的对象,所以你不能调用子方法。
同样,你持有基类引用 人类英雄=新骑士();
可见/可访问的方法取决于您持有对象而不是对象的引用变量的类型。
所以如果你改成它 骑士英雄=新骑士(); 所有方法都可以访问(基类和子类)。
答案 2 :(得分:0)
为什么我无法访问Knight类中声明的任何方法 使用参考变量
因为在编译时,编译器只知道所使用的引用,所以只有引用类型类中可用的方法可用。
Poplymorphism也称为动态绑定,因为它是在运行时根据对象决定的,将调用哪个类方法。
答案 3 :(得分:0)
您只能从声明左侧的类型访问方法。 Java看到了对象'英雄'作为一个人而不是一个骑士。这有助于迭代器中的类型安全性等(我认为比这个问题更先进)。这是一个安全功能,可确保实现某些方法,但也可以覆盖或不覆盖。
因此只有左手声明中的方法可用。它现在看起来很烦人,但在更高级的多态问题中,这是一种非常好的类型安全性。
答案 4 :(得分:0)
每个班级和每个界面都定义合同。合同由公共方法定义(非常简化)。
契约意味着您可以在扩展类或实现接口的对象上调用哪些方法。如果将对象(Car)存储到不同类型的引用(Vehicle):
Vehicle v = new Car();
你基本上是说把车当作车辆。一旦你说你使用的是车辆合同而不是汽车合同。
这意味着您只能调用车辆中定义的方法,而不能调用Car中的新方法。
答案 5 :(得分:0)
因为它打破了合同。
让我们假设(只是为了理解原因)Java允许您在Knignt
类引用上调用Human
类方法,并发布public
方法,如
public void doHumanThings(Human being) {
...
// but somewhere inside the method you call
being.doKnightThings();
...
}
这打破了合同,因为其他类不能简单地传递Human
对象,如
Human onlyHuman = new Human();
someObj.doHumanThings(onlyHuman);
那是因为如果他们这样做,你的代码在运行时会在调用
时中断onlyHuman.doKnightThings(); // ERROR!
因为所有Human
不能Knight
s 。
所以,虽然您已经向其他Java世界类(在public
类中使用public
方法)声明了您期望Human
个对象,但是#39;重新打破合同,期望它的行为类似于Knight
,其他客户类可能甚至不知道。
您不能简单地假设Human
引用将始终指向Knight
对象。您可以强制执行的唯一方法是声明Knight
类型的引用。
但是,如果方法实现想要主要使用父类行为但是在接收到子类对象的情况下添加或调整,那么instanceof
检查后的显式向下转换可以作为
public void doHumanThings(Human being) {
being.doHumanThings();
...
// Is this Human a Knight too?
if (being instanceof Knight) {
Knight iAmAKnightToo = (Knight) being;
// OK. Using a Knight reference now.
iAmAKnightToo.doKnightThings();
}
}
答案 6 :(得分:0)
Human hero = new Knight();
人类 =参与类型,英雄 =参考变量, 新骑士() =对象类型
如果你调用覆盖方法;对象类型决定可以调用哪个方法。
如果你调用非重写方法;引用类型决定了可以调用哪种方法。