Java中的继承,外观类型与实际类型

5

有人能解释一下这个执行结果吗?我无法注意到为什么会调用每个方法。我们如何区分真实类型和表面类型。谢谢 :)

public class JavaApplication15 {
   public static void main(String[] args) {
       A a = new A();
       B b= new B();
       A ab = new B();
       b.f(a);
       b.f(b);
       b.f(ab);
  }  
}

public class A {
   private final String id="A";
   public void f(A x){
     System.out.println("Send a fax to "+ x ) ;
  }
  public String toString(){return id;}
}

public class B extends A{
   private final String id="B";
   public void f(B x){
     System.out.println("Send an email to"+ x ) ;
   }
   public String toString(){return id;}
}

结果:

Send a fax to A
Send an email to B
Send a fax to B
4个回答

3

您正在过载,这会创建具有不同实现的相同名称的多个方法(根据维基百科)。我认为您想要使用f(B x)方法覆盖f(A x)方法。覆盖(根据维基百科)允许子类或子类提供已由其超类或父类提供的方法的特定实现。

根据您问题中的惊讶表达,我认为您想要类似于

@Override
public void f(A x) {
  System.out.println("Send an email to"+ x ) ;
}

2
@Override
public String toString()
{
    return id;
}

这定义了类的输出方式。当您调用以下函数时:

b.f(a)

您正在将一个类作为参数输入。由于您的toString()方法是覆盖了标准方法以输出您的类,因此您会在发送电子邮件至之后显示id


2
一个类 B 的对象既有 f(A) 也有 f(B)。当你调用 f(x) 时,它通常会选择与 x 类型匹配的最具体的那个。对此,重要的是引用的类型。
因此,你的 a 是一个 A,因此它调用 f(A)。你的 b 是一个 B,因此它调用 f(B)。但是你的 ab 是什么?引用的类型是 A。所以编译器告诉它调用 f(A) 的重载方法。
但是,难道它不应该将 ab 对象作为 A 使用,并使用 A.toString() 而不是 B.toString() 来打印 send a fax to A 吗?
不,因为在这种情况下调用的方法选择不是由参数决定的。这不是上面所说的重载之间的选择,而是覆盖之间的选择。在决定使用哪个方法(超类还是子类)时,对象的实际类型决定了它。由于 ab 引用的对象实际上是一个 B,因此选择了 BtoString() 方法。
因此,在重载的方法中,所选择的方法基于参数的数量和类型,并且参数的类型是引用的类型。
覆盖的方法中,所选择的方法是对象实例的实际类型,而不考虑通过其调用的引用类型。
请注意,仅当子类方法与父类方法具有相同的名称、参数数目和相应的参数类型时,才认为该方法被覆盖

1

由于B扩展了A,而A有一个公共方法。因此它具有以下两种方法 -

public void f(A x){
     System.out.println("Send a fax to "+ x ) ;
} 

和 -
public void f(B x){
     System.out.println("Send an email to"+ x ) ;
}

实际上,B 中的第二种方法是从 A 继承而来的重载版本的方法 void f(A x)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接