使用类型擦除覆盖方法

10

今天我偶然发现了一些有趣的东西。 假设以下Java 6类:

public class Ereasure {

    public Object get(Object o) {
        return null; // dummy
    }

    public  static class Derived<T> extends Ereasure{
        // (1)
        @Override
        public Object get(T o) {
                return super.get(o);
        }
        // (2)
        /*
        @Override
        public Object get(Object o) {
                return super.get(o);
        }*/

    }
}

如果您尝试编译上面的示例,编译器会报错: Ereasure.java:9: method does not override or implement a method from a supertype @Override 如果您删除 @Override 注释(其实不需要!),它会说: Ereasure.java:8: name clash: get(T) in Ereasure.Derived and get(java.lang.Object) in Ereasure have the same erasure, yet neither overrides the other 这有点矛盾,因为 T 应该擦除为 Object 并覆盖父类的 get 方法。
如果您保留 (1) 不加注释并取消对 (2) 的注释,以便 (1) 重载 (2),那么也不会起作用。 编译器输出:
Ereasure.java:15: get(T) is already defined in Ereasure.Derived
  public Object get(Object o) {

作为结论,T正在被擦除为Object,但无法覆盖父类的get方法。
我的问题是,为什么至少有一个示例不能编译?

@toto2 我认为你是对的,但我也认为这是一个有趣的看似未记录的边角情况。根据Type Erasure上的sun编译器注释,Derived中的方法定义应将Derived的get中的无界类型转换为public Object get(Object o),由于它是父类的子签名,因此应该覆盖。我认为他是正确的,当将这个东西转换成字节码时,无法解决由无界类型导致的方法分派歧义。 - nsfyn55
3个回答

7
您可以从下面的示例中看出为什么无法做到您想要的:
public class Erasure {

   public void set(Object o) {
      return;
   }

   // method overloading: (which is valid)
   public void set(String s) {
      return;
   }

   public static class Derived<S> extends Erasure {

      // Oops... which one am I supposed to override?
      // (It would actually be overloading if S was a concrete type
      // that is neither Object nor String.)
      @Override
      public void set(S o) { // does not compile
         super.set(o);
      }
   }
}

你的问题的解决方案是让Erasure成为一个带参数的类。

2

简单猜测,编译器在计算重载时不使用通用视图,这当然是没有意义的,因为有时T可能是Object,而其他时候则是另一种类型。那么覆盖将变得依赖于一个移动的目标T,这是完全错误的,特别是如果有多个方法都称为“get”,但具有不同的单参数类型。在这种情况下,这是毫无意义的,而且他们选择保持简单。


但总的来说,每个泛型都是一个对象,因为每个Java类都是从Object派生的... - user3001
以上并不完全正确,其中 T 的下限是 Object,其他声明可能会使用不同的下限。 - mP.

1

考虑一个情况,您同时重载了一个泛型的 getter 和 setter。

Derived<String> d = new Derived<String();
Erasure e = d;
e.set(new Object());
String s = d.get(); //Class cast exception

泛型的基本原则是,只有在存在(a)显式转换或(b)警告时,才会发生类转换异常。如果允许您想要做的事情,上述代码将在没有任何一个条件的情况下抛出异常。

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