在超类型对象上强制转换在java中返回类型子类的对象

时间:2014-12-30 19:10:49

标签: java binding casting subclass superclass

我已经实现了以下代码,以便理解静态和动态绑定之间的区别:

class A {

    int met(A a) {
        return 0;
    }

    int met(B b) {
        return 1;
    }

    int met(C c) {
        return 2;
    }
}

class B extends A {

    int met(A a) {
        return 3;
    }

    int met(B b) {
        return 4;
    }

    int met(C c) {
        return 5;
    }
}

class C extends B {

    int f() {
        return ((A)this).met((A)this);
    }
}

public class Test {

    public static void main(String[] args) {

        C x = new C();
        System.out.println(x.f());
    }
}

我得到的结果是3,但我不明白为什么,因为第一次演员是为了A。

2 个答案:

答案 0 :(得分:6)

所以,让我们来看看电话:

((A)this).met((A)this);

相当于:

A target = this;
A argument = this;
target.met(argument);

因此,对于最后一行,编译器根据所涉及的编译时类型查找签名 - 它将在A(和超类)中查找名为{的方法{1}},其参数与met(参数的编译时类型)兼容。重载决议发现答案是:

A

在编译时确定了签名。但是,该方法的实现是在执行时根据方法调用的执行时间目标确定的。该类型在此int met(A a) - 因为C是对this实例的引用。 (在C的实例上调用f方法。)

现在C不会覆盖C,但是int met(A a)(它的超类)会这样做 - 这就是所使用的实现。 B 覆盖Bmet(B b)并不重要,因为编译器已经确定它是met(C c)正在调用的方法。

答案 1 :(得分:2)

这是一个价格的两个问题。 (1)为什么我们从B类获得实现,以及(2)为什么我们得到带有A类参数的方法版本。

对于问题(1),要记住的是,对象的类在投射时不会改变,或者将其分配给类型不同的变量。因此,在您的示例中,this的类始终为C,因为这是您创建的内容。类C从类met(A a)继承其B版本,因为它没有自己的版本,并且因为类B已覆盖类{{}中的版本1}}。这就是多态性的全部内容 - 方法的版本取决于您调用它的对象的,而不是您使用的表达式的类型叫它。

对于问题(2),Java的一个小问题是在编译时评估方法签名。因此编译器会发现您已将 type A的表达式传递给您的方法,因此它会选择签名A。因为这个决定是在编译时做出的,所以参数的实际类没有任何区别 - 编译器已经根据表达式的类型选择了该方法。换句话说,Java 不会为您提供方法参数的多态性。