在 Java 中键入擦除

Type erasure in Java

我必须处理嵌套集合(例如,列表的地图或地图的地图),这就是我要处理它的方式:

  public static String doSomething(Map<?, ? extends Collection<?>> map) {
     ...
  }

  public static String doSomething(Map<?, ? extends Map<?, ?>> map) {
     ...
  }

但是编译器告诉我,上面两个方法的类型擦除是一样的。我想知道为什么,因为我指定了不同的类型范围。

无法重载每个重载的形式参数类型擦除为相同原始类型的方法。

在您的代码中,两种方法在类型擦除后具有相同的签名:

public static String doSomething(Map map);

要解决您的问题,您可以只使用两个不同的方法名称,而不是重载该方法。

擦除Map<?, ? extends Collection<?>>Map<Object, Object>

擦除Map<?, ? extends Map<?, ?>>也是Map<Object, Object>

要理解为什么,你需要了解Map的擦除是如何计算的。基本上,您采用类型 (Map<K, V>) 并将 formal 类型参数(而不是 actual 类型参数)替换为它们各自的 最小上限 类型。在这种情况下,KV 的最小上限类型都是 Object,因为它们都没有任何类型约束......在 Map 接口中。

我想我可能编造了这个词"least upper bound type"。 (抱歉)但我的意思是最具体的类型,它不是集合中允许的任何可能类型的子类型。


另一种理解擦除的方式如下。考虑这个 class:

public class Test <T> {
    public set(T t):
}

现在想象一下,我们必须在不使用泛型的情况下表达它。我们将使用什么实际类型来代替 T?在这种情况下,它将是 Object.

事实上,当泛型类型映射到运行时类型时,正是会发生什么!


但是,基本上,您将无法创建仅在 Map 类型的类型参数化上有所不同的方法的重载。除非你具体化类型:

  public class X implements Map<String, Integer> ...

  public class Y implements Map<String, Double> ...

  public static String doSomething(X map) {
     ...
  }

  public static String doSomething(Y map) {
     ...
  }

...至少可以说是丑陋的。

解决方案:使用不同的方法名称,而不是尝试重载相同的名称。