这是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版本。
答案 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参数不相同。