擦除如何处理Java中的覆盖场景?

时间:2014-02-10 23:45:28

标签: java generics override

这个问题来自我之前在here.的帖子。在发布我的问题之前,我正在粘贴来自oracle docs的内容;

8.4.8.1. Overriding (by Instance Methods)

An instance method m1, declared in class C, overrides another instance method m2, declared in class A iff all of the following are true:

    C is a subclass of A.

    The signature of m1 is a subsignature (§8.4.2) of the signature of m2.

8.4.2. Method Signature 
The signature of a method m1 is a subsignature of the signature of a method m2 if either:

    m2 has the same signature as m1, or

    the signature of m1 is the same as the erasure (§4.6) of the signature of m2.

我在type erasure涉及overriding时的理解如下: 如果在erasure之后,signature of m1 and m2相同,那么它们将被视为overridden。 所以在上一篇文章中,我尝试了override一个parent class method List<String>的{​​{1}} subclass methodList<Integer>之后假设type erasure 剩下的就是List<Object>。但那是错的。所以我对上面的定义有所了解 涉及overriding时方法erasure的完全错误。有些人可以简单一点 举例解释上述观点。

感谢。顺便说一句,以上几点来自here.

2 个答案:

答案 0 :(得分:3)

方法是否覆盖其他方法不仅仅处理方法的删除。编译器确定方法是否覆盖另一个方法,并且在类型擦除发生之前它可以访问所涉及的泛型类型参数。

您对使用擦除来确定覆盖的想法并不完全正确。让我们在讨论中添加以下JLS Section, 8.4.8.1

  

在类C中声明的实例方法m1将覆盖在类A中声明的另一个实例方法m2,如果以下所有条件都为真:

     
      
  • C是A的子类。

  •   
  • m1的签名是m2签名的子签名(§8.4.2)。

  •   
  • 或者:

         
        
    • m2在与C相同的包中公开,受保护或声明为默认访问权限,或

    •   
    • m1会覆盖方法m3(m3与m1不同,m3与m2不同),这样m3会覆盖m2。

    •   
  •   

要求m1m2的副标记,但不是相反。

示例1:

class A {
   public void foo(List<String> list) { }
}

class B extends A {
   @Override
   public void foo(List list) {}
}

这是合法的,因为B的{​​{1}}方法的签名与foo的{​​{1}}方法的删除相同。

示例2:

A

这是合法的,因为签名是相同的(即使它们是原始的)。

示例3:

foo

这不合法,因为不考虑重写方法的删除。也就是说,将class A { public void foo(List list) { } } class B extends A { @Override public void foo(List list) {} } class A { public void foo(List list) { } } class B extends A { @Override public void foo(List<Integer> list) {} } 的删除进行比较,List<Integer>仍然只是List,并且它们不相同。

示例4:

List

这也是不合法的,因为没有考虑重写方法的删除。也就是说,class A { public void foo(List<String> list) { } } class B extends A { @Override public void foo(List<Integer> list) {} } List<Integer>List<String>)的删除进行比较,并且它们不相同。

您无法在重写方法中更改参数的泛型类型参数(例如ListList<String>。在覆盖不使用泛型的方法时,不能引入泛型(例如({{ 1}}到List<Integer>)。但是,您可以在覆盖时删除泛型(例如ListList<Integer>)。

答案 1 :(得分:2)

因为Java是一种严格类型的语言,所以必须注意协方差以及类型如何协同工作。删除不是打破类型规则的借口。

例如:

class BaseGood<T> {
    public void doStuff(T elem) {
                // ...
    }
}
class DerivedGood<T> extends BaseGood {
    public void doStuff(Object elem) {
        super.doStuff(elem);
    }
}
class BaseBad {
    public void doStuff(List<Double> list) {
        // ...
    }
}
class DerivedBad extends BaseBad {
    public void doStuff(List<Integer> list) {
        super.doStuff(list);
        // ...
    }
}

在这里,我们有两种不同的擦除案例。

使用BaseGoodDerivedGood类,这两种方法具有相同的删除:void doStuff(Object elem)并且可以知道T将始终为{{1}类型因此该函数是类型安全的。

使用ObjectBaseBad类,这两种方法具有相同的删除:DerivedBad;但是,类型系统无法安全地从void doStuff(List list)转换为List<Integer>(或者根据此问题转换为任何List<Double>)。允许此转换可能会尝试将List放入Double的列表中(或者泛型将检测到编译时的问题)。

擦除后方法覆盖友好并不意味着类型系统无法检查不兼容性是否擦除。

修改

此外,应用于类/方法定义的 real 擦除不会在编译时发生,而是在运行时发生。引用泛型方法的代码只是编译,好像函数是在没有泛型的情况下调用的,由编译器和运行时检查强制执行类型安全。

请参阅:Java generics - type erasure - when and what happens

还有一种情况是您将类型擦除的泛型类传递给覆盖,并且因为类型系统无法检查您是否根据方法的类型传递了正确的列表,所以它必须允许它。