泛泛地重写 Java 中的方法

如果我有一个这样的基类,我无法更改:

public abstract class A {
    public abstract Object get(int i);
}

我尝试用这样的类来扩展它:B

public class B extends A{
    @Override
    public String get(int i){
        //impl
        return "SomeString";
    }
}

一切都很好。但是,如果我尝试,我试图使它更通用的尝试失败了:

public class C extends A{
    @Override
    public <T extends Object> T get(int i){
        //impl
        return (T)someObj;
    }
}

我想不出任何理由为什么应该禁止这样做。在我的理解中,泛型类型绑定到一个 —这是请求的返回类型。如果我可以把 或 作为我的返回类型 里面,为什么我不被允许放进我的班级?TObjectAStringAnyObjectB<T extends Object> TC

另一个奇怪的行为,从我的角度来看,是另外一个像这样的方法:

public class D extends A{

    @Override
    public Object get(int i){
        //impl
    }

    public <T extends Object> T get(int i){
        //impl
    }
}

也是不允许的,带有提供的提示。至少这个让我感到困惑,我认为Java应该做出决定:如果它是相同的返回类型,为什么不允许重写;如果不是,为什么我不能添加此方法?告诉我它是一样的,但不允许它被覆盖,这是非常奇怪的,基于常识。DuplicateMethod


答案 1

JLS # 8.4.2.方法签名

方法 m1 的签名是方法 m2 签名的子签名,如果出现以下任一情况:

  • m2 具有与 m1 相同的签名,或者

  • m1 的签名与 m2 签名的擦除 (§4.6) 相同。

根据上述规则,您的父母没有擦除,而您的孩子有一个,因此它不是有效的覆盖。

JLS#8.4.8.3.覆盖和隐藏中的要求

例 8.4.8.3-4.擦除影响覆盖

一个类不能有两个具有相同名称和类型擦除的成员方法:

class C<T> {
    T id (T x) {...}
}
class D extends C<String> {
    Object id(Object x) {...}
}

这是非法的,因为 D.id(Object)是D的成员,C.id(String)是在D的超类型中声明的,并且:

  • 这两种方法具有相同的名称 id
  • C.id(字符串)可由 D 访问
  • D.id(对象)的签名不是 C.id(字符串)的签名的子签名
  • 这两种方法具有相同的擦除

一个类的两种不同方法不能覆盖具有相同擦除的方法:

 class C<T> {
     T id(T x) {...}
 }
 interface I<T> {
     T id(T x);
 }
 class D extends C<String> implements I<Integer> {
    public String  id(String x)  {...}
    public Integer id(Integer x) {...}
 }

这也是非法的,因为 D.id(String)是D的成员,D.id(Integer)在D中声明,并且:

  • 这两种方法具有相同的名称 id
  • D.id(整数)可由 D 访问
  • 这两种方法具有不同的签名(并且都不是另一种方法的子签名)
  • D.id(String)覆盖 C.id(String)和 D.id(Integer)覆盖 I.id(Integer),但两个被覆盖的方法具有相同的擦除

此外,它还给出了允许从超级到儿童的情况的示例

子签名的概念旨在表达签名不相同但其中一个可以覆盖另一个签名的两个方法之间的关系。具体而言,它允许其签名不使用泛型类型的方法重写该方法的任何生成版本。这很重要,以便库设计人员可以独立于定义库的子类或子接口的客户端自由生成方法。

请考虑以下示例:

class CollectionConverter {
List toList(Collection c) {...}
}
class Overrider extends CollectionConverter {
 List toList(Collection c) {...}

}

现在,假设此代码是在引入泛型之前编写的,现在类 CollectionConverter 的作者决定生成代码,因此:

 class CollectionConverter {
   <T> List<T> toList(Collection<T> c) {...}
 }

如果没有特殊豁免,Overrider.toList将不再覆盖CollectionConverter.toList。相反,该代码将是非法的。这将大大抑制泛型的使用,因为库编写者会犹豫是否迁移现有代码。


答案 2

好吧,对于第一部分,答案是Java不允许非泛型方法被泛型方法覆盖,即使擦除是相同的。这意味着即使您只是将重写方法设置为:

 public <T extends Object> Object get(int i)

我不知道为什么Java会带来这种限制(经过了一些思考),我只是认为它与为泛型类型子类化实现的特殊情况有关。

你的第二个定义基本上可以翻译为:

public class D extends A{

    @Override
    public Object get(int i){
    //impl
    }

    public Object get(int i){
        //impl
    }

}

这显然是一个问题。