超类变量是否可以访问子类的重写方法。 例如:
class A {
void callMe() {
System.out.println("Inside A");
}
}
class B extends A {
void callMe() {
System.out.println("Inside B");
}
}
class Dispatch {
public static void main(String args[]) {
A a = new A();
B b = new B(); // Object of type B
A r; // Obtain a reference of type A
r = a; // Refers to A object
r.callMe(); // Calls A's version of callMe()
r = b; // Refers to B object
r.callMe(); // calls B's version of callMe() and my question is on this
}
}
我之前了解到,引用子类对象的超类变量只能访问由超类定义的对象的那些部分。那么第二个r.callMe()
调用B
的{{1}}版本怎么样呢?它应该只能再次调用callMe()
的{{1}}版本。
答案 0 :(得分:2)
...引用子类对象的超类变量只能访问由超类定义的对象的那些部分
这不完全正确。最后,运行时调用对象的实际类型,而不管引用类型如何。因此r.callme()
实际上会调用callme()
中定义的B
,因为r
是B
个对象。
new B(); // <-- The object in memory is of type B and its type never
// changes.
A a = new B(); // <-- The object in memory is of type B; the reference type
// is A. But that effectively does only matter at
// compile-time, I believe.
在上面的示例中, B
称为对象类型, A
称为< em>参考类型。
请参阅Java Language Specification § 15.12.4.4:
让
X
成为方法调用的目标引用的编译时类型。[...]
如果调用模式为
virtual
,并且S
中的声明会覆盖X.m
(§8.4.8.1),则S
中声明的方法就是方法被调用,程序终止。
让我粗略猜测它们的意思是“仅访问由超类定义的那些部分”:
class A {
void doSomething() { }
}
class B extends A {
void doAnotherThing() { }
}
A a = new B();
a.doAnotherThing(); // Not valid, because doAnotherthing()
// is defined in class B.
要调用doAnotherThing()
,必须使用类型转换:
((B) a).doAnotherThing(); // Valid
答案 1 :(得分:1)
在你的问题中
R = B;
现在r抓住“new B()”对象。当你调用r.callme()然后在B类中运行callme方法。因为r有B对象。
任何程序都会抛出编译时错误,因为超类的引用类型没有子类名称的方法。
作为例子
class Animal {
public void move() {
System.out.println("Animals can move");
}
}
class Dog extends Animal {
public void move() {
System.out.println("Dogs can walk and run");
}
public void bark() {
System.out.println("Dogs can bark");
}
}
public class TestDog {
public static void main(String args[]) {
Animal a = new Animal(); // Animal reference and object
Animal b = new Dog(); // Animal reference but Dog object
a.move(); // runs the method in Animal class
b.move(); // runs the method in Dog class
b.bark();
}
}
输出
TestDog.java:26: error: cannot find symbol
b.bark();
^
symbol: method bark()
location: variable b of type Animal
1 error
答案 2 :(得分:0)
Java方法是virtual,因此对象的运行时(实际)类型决定调用哪个方法,而不是变量的静态(声明)类型。
但是,变量的静态(声明)类型确定哪些方法(以及字段)可见,即您可以调用哪些方法。
答案 3 :(得分:0)
我认为你可能会在对象和变量之间产生一些混淆。变量可以被认为是“指针”。它只是指向一个对象 E.g。
A var = newA(); // var -> Object A
即使var1被定义为类型A,它也可以指向该类型的子类 E.g。
A var = new B(); // var -> Object B
现在,当你在一个变量上调用一个方法时,它会在它指向的任何对象上调用该方法(即使该对象是一个子类) E.g。
A var = new B(); // var -> Object B
var.someMethod(): // calls B.someMethod()
即使var
属于A类型,调用方法仍会调用B.someMethod()
,因为var
指向Object B
。
变量的所有类型都定义了变量可以指向的对象,因此在所有var
类型为A
的情况下,它意味着它只能 点到类型为A
或扩展A
的对象。
我希望这有帮助! :)
答案 4 :(得分:0)
因为,这是方法查找在Java中的工作方式。
r
。r
中的对象b
。B
。B
包含callMe()
方法。因此,执行此方法而不是超类callMe()
方法。如果callMe()
没有被覆盖,那么A
的{{1}}方法就会被执行。此外,这是多态性所必需的。例如: 比方说,你有一个超级水果,有子类Apple和Grape。
callMe()
现在,您希望将Apple和Grape的实例存储在同一列表中。
public class Fruit{
public String getName() {
return "Fruit";
}
}
public class Apple extends Fruit{
private String name;
public Apple(String name){
this.name = name;
}
public String getName(){
return name;
}
}
public class Grape extends Fruit{
private String name;
public Grape(String name){
this.name = name;
}
public String getName(){
return name;
}
}
现在,您想要遍历每个水果项目并获取其名称。
Fruit apple = new Apple("apple");
Fruit grape = new Grape("grape");
ArrayList<Fruit> fruits = new ArrayList<Fruit>();
fruits.add(apple);
fruits.add(grape);
在这种情况下,项目类型为Fruit并调用fruits.forEach(item -> {
System.out.println(item.getName());
});
:您希望从相应的子类getName
中获取名称,而不是来自超类Apple/Grape
的{{1}}值。