有没有可能在超类对象上调用子类的方法?
动物是狗和狗的超级有一种叫做树皮的方法
public void bark() { System.out.println("woof"); }
考虑以下几点:
Animal a = new Dog(); if (a instanceof Dog){ a.bark(); }
会发生什么?
- 该分配是不允许的
- 树皮的呼叫是允许的,并在运行时打印“woof”
- 树皮的呼叫是允许的,但没有打印
- 对树皮的调用导致编译时错误
- 对树皮的调用导致运行时错误
我说2,因为我们正在检查对象是否是狗; 因为狗是与树皮方法类,如果它是,那么我们把它打印出来:s
我的理解在这里正确吗?
这不会被编译,因为动物没有叫做树皮的方法。 想想这样,所有的狗都是动物,但不是所有的动物都是狗。 所有的狗叫,但不是所有的动物树皮。
不 – 答案是;
4)对树皮的调用会导致编译时错误
树皮方法并没有被定义为指定types的动物的方法,因此会导致编译时间问题; 这可以通过铸造解决;
((Dog)a).bark();
关键在于以下行:
Animal a = new Dog();
尽pipe创build了一个Dog
的新实例,但是它的引用是由a
被声明为Animal
types的实例引用的。 因此,任何提及a
使得new Dog
被当作Animal
来处理。
因此,除非Animal
有一个bark
方法,否则下面这行会导致编译器错误:
a.bark();
即使a
被testing以查看它是否为Dog
a instanceof Dog
,并且Dog
a instanceof Dog
实际上会返回true
,但variablesa
仍然是Animal
types的,所以if
语句中的块仍然将a
作为Animal
处理。
这是静态types语言的一个特性,其中variables被提前分配一个types,并且在编译时检查这些types是否匹配。 如果这个代码是在dynamictypes语言上执行的,那么在运行时检查这些types,就可以允许类似下面的内容:
var a = new Dog(); if (a instanceof Dog) a.bark();
a.bark()
只保证在实例是Dog
时候执行,所以对bark
的调用将始终有效。 但是,Java是一种静态types的语言,所以这种types的代码是不允许的。
这是4.你不能问一个通用的动物 – 这是你的代码说的是 – 吠叫。 因为你可以轻而易举地说
Animal a = new Cat();
树皮线没有办法知道你没有。
如果想法是从超类对象打印子类方法,这将工作:
而不是Animal a = new Dog(); if (a instanceof Dog){ a.bark(); }
Animal a = new Dog(); if (a instanceof Dog){ a.bark(); }
Animal a = new Dog(); if (a instanceof Dog){ a.bark(); }
改为
Animal a = new Dog(); if (a instanceof Dog){ Dog d = (Dog) a; d.bark(); }
这将超类转换回子类并打印它。 虽然它的devise不好,但它的一种方法就是知道哪个子类对象dynamic地指向它。
在Head First Java中,他们使用电视遥控器的非常好的类比作为参考 ,将电视作为参考指向的对象 。 如果您的遥控器只有button(方法)可以打开,closures,上下和上下调整音量,那么无论您的电视有什么酷炫function都无所谓。 你仍然只能从你的遥控器做这几件基本的事情。 例如,如果您的遥控器没有静音button,则无法将电视静音。
动物参考只知道动物的方法。 无关紧要的对象有什么其他的方法,你不能从一个Animal引用访问它们。
仅供参考,这不是一个好的devise。
几乎任何时候你有这种forms的代码:
if (x instanceof SomeClass) { x.SomeMethod(); }
你滥用types系统。 这不是使用类的方式,也不是编写可维护的面向对象代码的方法。 这是脆弱的。 这是错综复杂的。 这不好。
您可以在基类中创build模板方法,但必须调用基类中存在的方法,并在子类中重写。
在Java(只有我知道的语言),你可以创build一个空方法,并在超级类中调用它。 然后你可以在子类中重写它来做任何你想要的。 这样超类调用它的子类的方法。
public class Test { public static void main(String[] args){ Snake S = new Snake(); Dog d = new Dog(); } } class Animal{ //Super Class public Animal(){ bark(); //calls bark when a new animal is created } public void bark(){ System.out.println("this animal can't bark"); } } class Dog extends Animal{ //Subclass 1 @Override public void bark(){ System.out.println("Woof"); } } class Snake extends Animal{//Subclass 2 public void tss(){ } }
此代码调用Snake的对象,然后调用Dog的对象。 它将这写到控制台:
this animal can't bark Woof
Snake没有任何树皮方法,所以叫超类“方法。 它将第一行写入控制台。 狗有一个树皮方法,所以超类叫它。 它将第二行写入控制台。
“我说我们正在检查对象是否是一只狗;因为狗是带有树皮方法的类,如果是的话,我们会打电话给它打印出来:
你的理由是正确的,但这不是它的工作方式。
Java是一种静态types语言,意味着对象可以响应的方法的有效性在编译时被validation。
你可能会认为这个支票:
if( a instanceof Dog )
会做,但实际上没有。 编译器所做的是检查声明types的“接口”(在这种情况下为Animal)。 “接口”由Animal类声明的方法组成。
如果在超类Animal中未定义bark()方法,编译器会说:“嘿,那不行”。
这是有帮助的,因为“有时候”我们在编码时犯了拼写错误(例如,inputbarck())
如果编译器没有提醒我们这个问题,那么你就不得不在“运行时”find它,而不是一直用清晰的消息(例如IE中的javascript表示类似“意外对象”的东西)
尽pipe如此,Java这样的静态types语言允许我们强制调用。 在这种情况下,它使用“cast”运算符()
喜欢这个
1. Animal a = new Dog(); 2. if (a instanceof Dog){ 3. Dog imADog = ( Dog ) a; 4. imADog.bark(); 5. }
在第3行中,您正在“铸造”为Dogtypes,因此编译器可能会检查树皮是否是有效的消息。
这是对编译器的说明:“嘿,我是程序员,我知道我在做什么”。 而编译器,检查,Ok,dog,可以收到消息bark(),继续。 然而,如果在运行时动物不是狗,运行时exception将会boost。
演员阵容也可以缩写为:
if( a instanceof Dog ) { ((Dog)a).bark(); }
这将运行。
所以,正确答案是4: “ 树皮的调用导致编译时错误 ”
我希望这有帮助。