动态方法调度如何在Java中工作

时间:2017-03-28 06:41:27

标签: java polymorphism

超类变量是否可以访问子类的重写方法。 例如:

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}}版本。

5 个答案:

答案 0 :(得分:2)

  

...引用子类对象的超类变量只能访问由超类定义的对象的那些部分

这不完全正确。最后,运行时调用对象的实际类型,而不管引用类型如何。因此r.callme()实际上会调用callme()中定义的B,因为rB个对象。

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}}值。