除了泛型类型参数之外,为什么Java不允许使用相同的签名定义两个方法?

时间:2011-12-11 20:29:56

标签: java

  

可能重复:
  Method has the same erasure as another method in type

在我的一个课程中,我想定义这两个方法:

private void add(List<ChangeSet> changeSetList) {
    for (ChangeSet changeSet : changeSetList) {
        add(changeSet);
    }
}

private void add(List<Change> changeList) {
    for (Change change : changeList) {
        add(change);
    }
}

然后我收到以下错误:

Method add(List<Change>) has the same erasure add(List<E>) as another method in type DataRetriever

为什么不允许这样做?这样的方法定义有什么问题?我应该怎么做才能避免它呢?我不想重命名其中一种方法。

5 个答案:

答案 0 :(得分:11)

这就是Java的类型系统如何“工作”。泛型List<Change>List<ChangeSet>实际上并不是不同的类型。泛型参数只是编译器执行某些检查和某些强制转换的提示。然而,就JVM和类型系统而言,两种类型实际上都被“擦除”到List<Object>(或者只是List,如果你愿意的话),这两种类型实际上是相同的,没有内部差异。因此,您不能在不同的泛型参数上实际重载,因为就重载决策而言,这两种类型是相同的。

答案 1 :(得分:7)

此限制是语言语法的一部分,而不是Java运行时本身。本质上,此规则旨在避免仍使用原始类型的遗留代码中的冲突。

javac这样的编译器将拒绝这种类型的重载,但是如果你通过其他方式创建一个类(编写自己的编译器,或使用像ASM这样的字节码工程库),签名只有不同的类型参数,javac编译器将解析调用类中正确的方法。

以下是为什么不允许这样做的说明,drawn from the JLS.假设在将泛型引入Java之前,我写了一些这样的代码:

class CollectionConverter {
  List toList(Collection c) {...}
}

你扩展我的课程,如下:

class Overrider extends CollectionConverter{
  List toList(Collection c) {...}
}

引入泛型后,我决定更新我的库。

class CollectionConverter {
  <T> List<T> toList(Collection<T> c) {...}
}

您尚未准备好进行任何更新,因此您只能离开Overrider课程。为了正确地覆盖toList()方法,语言设计者决定原始类型与任何通用类型“覆盖等效”。这意味着虽然您的方法签名不再正式等于我的超类'签名,但您的方法仍然会覆盖。

现在,时间过去了,您决定准备更新课程。但是你搞砸了一下,而不是编辑现有的原始toList()方法,你添加这样的新方法:

class Overrider extends CollectionConverter {
  @Override
  List toList(Collection c) {...}
  @Override
  <T> List<T> toList(Collection<T> c) {...}
}

由于原始类型的覆盖等价,两种方法都以有效的形式覆盖toList(Collection<T>)方法。但是,当然,编译器需要解决单个方法。为了消除这种歧义,不允许类有多个覆盖等效的方法 - 即擦除后具有相同参数类型的多个方法。

关键是这是一种语言规则,旨在允许继续使用原始类型,而不是因类型参数的擦除而产生的限制。

如果您消除遗留代码(例如,使用您自己的,非严格Java语言),这种类型的重载功能就完美无缺。因为方法解析是在编译时发生的,所以在擦除之前,不需要输入类型来实现这一点。

答案 2 :(得分:2)

除了adarshr和Kerrek的答案之外,为什么不把它变成通用的,如下所示:

private <T> void add(List<T> changeList) {
    for (T change : changeList) {
        add(change);
    }
}

这应该适用于两种情况......

答案 3 :(得分:1)

因为泛型只是编译时帮助你。编译后,字节码中不会存储与泛型相关的信息。

看看这个:

http://docs.oracle.com/javase/tutorial/java/generics/erasure.html

答案 4 :(得分:0)

在类型擦除之后,两种方法都将具有private void add(List)的签名,这是不允许的。

您需要重命名方法或传递另一个参数,例如列表值的类。