键入擦除和覆盖通用方法

时间:2017-05-10 19:33:47

标签: java generics

我对泛型的理解是,如果我有public void saveAll(Collection<? extends Object> stuff) {那么编译将确定输入参数的“最常见类型”为Collection<Object>。现在,我在下面编写了用于覆盖泛型方法的代码,但是我在覆盖方法中得到以下错误消息 - Name clash: The method saveAll(Collection<? extends Object>) of type ChildWithGenericMethod has the same erasure as saveAll(Collection<Object>) of type ParentWithGenericMethod<T> but does not override it

public class ParentWithGenericMethod {
    public void saveAll(Collection<Object> stuff) {

    }
}

public class ChildWithGenericMethod extends ParentWithGenericMethod{
    public void saveAll(Collection<? extends Object> stuff) {

    }
}

我想知道的是在类型擦除之后这些方法会是什么样子,我认为它们看起来像public void saveAll(Collection stuff) {因此我应该能够覆盖,因为如果我不要使用泛型然后我可以这样覆盖。

请注意,我知道在使用“javap”编译后我可以看到字节代码,但它并没有告诉我在“类型擦除”之后这些方法会是什么样子。

<小时/> 的更新
这个问题没有回答我的问题,如果在类型擦除后两个方法都成为public void saveAll(Collection stuff) {那么为什么子类会得到重写错误,因为public void saveAll(Collection<Object> stuff) {它会传递重写规则。

2 个答案:

答案 0 :(得分:1)

值得记住运行时间与编译时调度。

带走仿制药一秒钟。

public class Parent {
  public void saveAll(Object x) {
  }
}

public class Child extends Parent {
   public void saveAll(String x) {
  }
}

public String x = "hello";
public Object y = new Integer(334);
public Object z = "goodbye";

public Child c1 = new Child();

c1.saveAll(x); // invokes Child.saveAll
c1.saveAll(y); // invokes Parent.saveAll
c1.saveAll(z); // invokes Parent.saveAll even though z contains a String

现在把仿制药带回故事。

 public class Child<T extends Object> extends Parent {
   public void saveAll(T x) {
  }
}

这里,saveAll重载父进程的saveAll而不是覆盖。

用极端的东西代替T?并没有真正改变这一点 - 编译器正在尝试创建一个重载父方法的方法,但后来意识到它不能在编译时类型上调度。

[编辑,请参阅Lew Block对原文的评论:编译器需要确定子方法是否覆盖或重载父方法类型擦除之前]

答案 1 :(得分:0)

  

如果在类型擦除之后两个方法都变为公共void voidAll(Collection stuff){那么为什么子类会得到重写错误,因为使用public void saveAll(Collection stuff){它传递了重写规则。

问题(再次参见Lew Bloch的评论)是在类型擦除之前确定的覆盖。在类型擦除之前,父方法和子方法具有不同的签名,因此编译器将它们创建为重载方法而不是重写方法。在类型擦除之后,编译器意识到它处于绑定状态,因为它现在有两个具有相同签名的方法,无法知道如何分派。