方法在类型中与另一种方法具有相同的擦除

2022-08-31 04:36:32

为什么在同一类中使用以下两种方法是不合法的?

class Test{
   void add(Set<Integer> ii){}
   void add(Set<String> ss){}
}

我得到compilation error

方法 add(Set) 与 Test 类型的另一个方法具有相同的擦除 add(Set)。

虽然我可以解决这个问题,但我想知道为什么javac不喜欢这个。

我可以看到,在许多情况下,这两种方法的逻辑将非常相似,并且可以被单个方法所取代。

public void add(Set<?> set){}

方法,但情况并非总是如此。

如果你想有两个接受这些参数,这是额外的烦人,因为这样你就不能只更改其中一个.constructorsconstructors


答案 1

此规则旨在避免仍使用原始类型的旧代码中的冲突。

以下是为什么不允许这样做的说明,来自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) {...}
}

您还没有准备好进行任何更新,因此您就不理会您的课程了。为了正确重写该方法,语言设计人员决定原始类型与任何生成的类型“重写等效”。这意味着,尽管您的方法签名不再正式等于我的超类签名,但您的方法仍然会覆盖。OverridertoList()

现在,时间流逝,您决定已准备好更新课程。但是你搞砸了一点,而不是编辑现有的原始方法,你添加了一个新方法,如下所示:toList()

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

由于原始类型的重写等效性,这两种方法都采用有效形式来重写该方法。但是,当然,编译器需要解析单个方法。为了消除这种多义性,不允许类具有多个重写等效的方法,即,在擦除后具有相同参数类型的多个方法。toList(Collection<T>)

关键是,这是一个语言规则,旨在使用原始类型保持与旧代码的兼容性。它不是擦除类型参数所要求的限制;由于方法解析发生在编译时,因此将泛型类型添加到方法标识符就足够了。


答案 2

Java 泛型使用类型擦除。尖括号( 和 ) 中的位将被删除,因此您最终会得到两个具有相同签名的方法(您在错误中看到的)。这是不允许的,因为运行时不知道对每种情况使用哪个。<Integer><String>add(Set)

如果Java得到了重新定义的泛型,那么你可以这样做,但现在可能不太可能。


推荐