在此代码中如何选择一种方法而不是另一种方法?

时间:2010-09-25 18:36:55

标签: java inheritance polymorphism

这是SCJP的另一个问题。下面的代码打印Alpha:fooBeta:fooBeta:barBeta:bar,我不明白为什么第一个foo调用选择Alpha的foo而不是Beta。如果Alpha.foo参数更改为String而不是String ...,则输出为Beta:fooBeta:fooBeta:barBeta:bar,这是有道理的。

我的理解是,当你说Alpha a = new Beta();时,编译器检查Alpha.foo,但JVM实际上会运行Beta.foo。首先,Beta确实有一个foo方法,其签名与调用匹配。另一方面,我认为varargs方法只在没有其他可用的方法与调用匹配时运行。所以这就是我认为不应该运行Alpha.foo的两个原因。这种理解的哪一部分是错的?

谢谢!

class Alpha {

    public void foo(String... args) { //if this were String args, then it makes sense
        System.out.print("Alpha:foo");
    }

    public void bar(String a) {
        System.out.print("Alpha:bar");
    } }

public class Beta extends Alpha {

    public void foo(String a) {
        System.out.print("Beta:foo");
    }

    public void bar(String a) {
        System.out.print("Beta:bar");
    }

    public static void main(String[] arg) {
        Alpha a = new Beta();
        Beta b = (Beta) a;
        a.foo("test");//confusing line
        b.foo("test");
        a.bar("test");
        b.bar("test");
    } }

编辑:我想我知道现在我误解了什么。我认为在SuperClass sc = new SubClass();情况下,无论在sc上调用什么方法都会在运行时在SubClass中搜索,尽管它们将在编译时在SuperClass中搜索。事实证明,正如我现在认为的那样,无论在编译时和运行时,在SuperClass中搜索sc上调用的任何方法,UNLESS SubClass都提供了“更好”的版本方法。然后,即使在编译时,编译器也会知道要调用的方法是SubClass版本。

5 个答案:

答案 0 :(得分:6)

带有String[]String...参数的方法和带有String参数的方法不共享相同的签名。字符串数组是完全独立的类型,因此它是标准的重载,并且不会发生多态。编译器看到它是“Alpha”类型的实例,并调用那里找到的foo方法。 Beta中的foo方法未被调用,因为它实际上并不是Alpha版本的覆盖。

(这是@Override注释派上用场的地方。如果您试图在Beta版foo上使用它,则会出现编译错误。)

答案 1 :(得分:4)

方法重载是在编译时实现的,方法覆盖 - 在运行时。

在您的情况下,foo中的Beta不会覆盖Alpha中的foo。它使用不同的参数重载它(您可以通过添加@Override注释来证明这一点,这是检测此类不那么明显的潜在问题的最佳实践。)

另一个偷偷摸摸的事情是你无法从Alpha#foo(..)的引用中调用Beta - 即b.foo("test")将始终调用非varargs方法。至于发生这种情况的原因,请参阅this question

答案 2 :(得分:2)

由于a和b“foo()”方法没有相同的签名,b“foo()”方法不会覆盖“foo()”方法。

因此,当你调用“a.foo()”时,Alpha.foo()。

要验证这一点,您可以尝试在“b.foo()”方法上添加“@Override”注释。这将导致编译错误(因为“b.foo()”不会覆盖任何内容)。

答案 3 :(得分:2)

当你说Super a = new Sub();并做一个a.methodCall();以下规则适用..

查看子类是否有一个方法覆盖超类中的方法并调用它。

但是这里子类中的方法没有覆盖,但是在super中重载了该方法,因此调用了超类的实现。当你对两个方法的参数相同时。然后覆盖原则适用。

答案 4 :(得分:0)

覆盖在运行时出现,在你的情况下,你不会覆盖foo方法,因为超类和子类中的方法参数是不同的。具有String[] or String...参数的方法和方法String参数不相同。