使用类型擦除覆盖方法

时间:2011-08-31 14:05:18

标签: java generics jvm

今天我偶然发现了一些有趣的事情。 假设以下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:方法不会覆盖或实现超类型的方法         @覆盖 如果你删除@Override注释(这不应该是必要的!),它说     Ereasure.java:8:名字冲突:get(T)在Ereasure.Derived和Ereasure中的get(java.lang.Object)有相同的擦除,但都没有覆盖其他 这有点矛盾,因为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方法。

我现在的问题是,为什么至少有一个例子没有编译?

3 个答案:

答案 0 :(得分: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应该是参数化类。

答案 1 :(得分:2)

简单地猜测编译器在计算重载时不使用通用视图,这当然是没有意义的,因为有时T可能是Object,而其他类型也是。然后,重叠将依赖于移动目标T,这是完全错误的,特别是如果有多个方法都被称为“获取”但具有不同的单个参数类型。在这种情况下,它只是没有意义,并且猜测他们选择保持简单。

答案 2 :(得分: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)警告时才会发生类强制转换异常。如果你被允许做你想做的事情,上面会抛出一个例外而没有。