2012-03-13 100 views
4

我想知道为什么的Java:重载方法

z.f(-6); 

在M级的呼叫是指下列函数B类:的

public void f(double y) { 
    this.x = (int) y + B.y; 
} 

,而不是在A级使用函数f,因为bx由A覆盖。或者使用

public void f (int y) { 
    this.x = y*2; 
    B.y = this.x; 
} 

在类B中,至少参数类型匹配。

完成以下代码:

public class A { 
    public int x = 1; 
    public A(int x) { 
     this.x += x; 
    } 
    public A (double x) { 
     x += x; 
    } 
    public void f(double x) { 
     this.x = this.x + (int) (x + B.y); 
    } 
} 

public class B extends A { 
    public static int y = 3; 
    public int x = 0; 
    public B (double x) { 
     super((int) x); 
    } 
    public void f(int y) { 
     this.x = y*2; 
     B.y = this.x; 
    } 
    public void f(double y) { 
     this.x = (int) y + B.y; 
    } 
    } 

public class M { 
     public static void main (String[] args){ 
     A a = new A(B.y); 
     a.f(1); 
     B b = new B(3.0); 
     A z = b; 
     z.f(-5.0); 
     z.f(-6); 
     System.out.println(b.x + " " + z.x); 
     } 
    } 
+0

我觉得Mike Samuel的回答很好,因为z有类型A,尽管它拥有A的一个子类B的实例,但只有A的方法可以被调用;对于子类型B的方法的调用,你将不得不downcast z(尽管使用checked downcast,例如使用instanceof,因为否则可能会遇到运行时异常)。 - 在一个不相干的笔记上,既然我看到了你对理论计算机科学的简介,并且正在考虑在RWTH做我的主人,那么在那里学习怎么样?从您对TCS的问题来看,听起来有些苛刻? – 2012-03-13 23:51:48

+0

编辑:我误读了麦克塞缪尔的答案,在他的第一句话,它需要阅读“A.f(双)”。一个正确的,不太详细的答案(这也是不太需要,而仍然捕捉问题)将是悲惨变量的;也检查那一条的评论。 – 2012-03-14 00:33:56

+0

@ G. Bach:我可以强烈推荐RWTH大学。这很有挑战性,但很有趣。我只是在我的第一个学期,所以我的经验是相当有限的;-) – Laura 2012-03-15 18:46:09

回答

3
z.f(-6); 

静态类型的zA,它只有一个名为f一种方法。该方法需要double参数,可以提升文字值-6。所以在编译时,调用绑定到A.f(double)

在运行时z被发现是B类型,它有自己的B.f(double)覆盖A.f(double)的,所以调用这一点,得到该方法。

+0

我的印象是,如果变量被转换为子类,调用只会绑定到子类方法 - 然而,一个简单的测试表明,你是对的,即使在编译时调用绑定到Af(double ),它在运行时绑定到Bf(double),因为z是B类型的一个实例。 – 2012-03-14 00:06:40

+0

@ G.Bach是的,这就是'overriding'的全部要点,用于根据动态类型确定要调用的方法。 – 2012-03-14 00:10:39

+0

那么我是否正确理解这一点,即重载方法名称绑定的方法是在编译时决定的,而哪个类的重写(然后已经选择)方法的问题是在运行时决定的?我所尝试的是:如果添加一个方法Af(int),调用z。( - 6)将以Bf(int)结尾,那么对于那个调用似乎发生的事情是,在编译时,编译器选择Af(int),因为z在类型A的变量中,而在运行时,这会被Bf(int)覆盖。我没有意识到这一点。 – 2012-03-14 00:24:22

3

静态类型的zA这样z.f(-6)可以仅结合在A的方法,在这种情况下是A.f(int)

该语言的设计,这种方式使

A z = new B(3.0); 
z.f(-6); 

总会表现得一样

A z = complicatedWayToComputeTrue() ? new B(3.0) : new A(3.0); 
z.f(-6); 

如果编译器绑定到一个不同的方法签名,因为它可以证明A z总是持有一个B然后这会引起各种非本地影响的语言,使它真的很难调试或维护Java程序。

想象有人试图维持

final A z = complicatedWayToComputeTrue() ? new B(3.0) : new A(3.0); 
// 1000 lines elided 
z.f(-6); 

通过改变它

A z = new B(3.0); 
// 1000 lines elided 
z.f(-6); 

如果编译器现在可以证明A始终是一个B并结合Z.f的方法B,维护者会感到困惑。

+0

这个答案的第一句话(和主要内容)是**完全不正确**。当然,当它的类型被超类引用时,你可以在子类上调用一个方法 - 这是OOP的本质! – Bohemian 2012-03-13 23:31:24

+0

@波希米亚,试试这个程序。 (class A {} class B extends A {void f(){} public static void main(String [] args){A x = new B(); A.foo(); }}。你会得到一个编译错误:“找不到符号方法foo()”。 – 2012-03-13 23:34:40

+0

你们都是对的,视情况而定。如果重写子类中的方法,那么即使在引用父类的实例时,如果重载或创建新方法,它显然也会被绑定,而不是太多。 – 2012-03-13 23:38:19

1

Java是单一调度,而您试图执行的操作是double dispatch(调用的方法取决于动态运行时类和参数)。

在Java中调用方法的签名是在编译时确定的;这意味着对象的声明类决定了绑定的方法。覆盖子类中的方法会影响绑定的实现,但重载方法不会(因为重载的方法具有不同的签名)。

在类B中,当使用一个声明为A类的对象时,使用接受int的版本重载f(),但此方法看起来并不存在(您不能调用它,也不会调用)。

总结:

  • 编译器结合在编译时间的方法签名。
  • 此绑定签名取决于对象和参数的声明(编译时)类型。
  • 如果在子类中重写方法,则在调用子类时(不管声明的类型如何),将选择重写的方法。
  • 重载不是重写,重写你需要相同的方法签名(以及除协方差外)。
0

嗯,我可能是错的,但如果你A类型的对象z链接到B类型的对象,它仍然会绑定为B类型,这就是为什么它的执行方法B类,而不是在A类。请注意,由于您没有使用new,因此您并未创建A类型的对象。

而且正如Mike Samuel所说,默认-6应该被认为是int,但这不符合您的解释。我会尽力找出适当的答案。

+0

不是“默认”。在Java中,'-6'总是**一个'int'文字。 Java没有像[Go](http://golang.org/doc/go_spec.html#Constants)这样的任意精度常量,其中“数字常数表示任意精度的值并且不会溢出”。 – 2012-03-13 23:46:53