一般用Java覆盖一个方法

Raf*_*l T 23 java generics overriding casting type-conversion

如果我有这样的基类,我无法改变:

public abstract class A {
    public abstract Object get(int i);
}
Run Code Online (Sandbox Code Playgroud)

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

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

一切都好.但是如果我尝试的话,我试图让它更通用的失败:

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

我想不出有什么理由不应该被禁止.根据我的理解,泛型类型T绑定到Object- 这是请求的返回类型A.如果我可以把StringAnyObject作为我的回归类型B,我为什么不被允许<T extends Object> T进入我的C班级?

从我的观点来看,另一个奇怪的行为是这样的另一种方法:

public class D extends A{

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

    public <T extends Object> T get(int i){
        //impl
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Ami*_*nde 10

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) {...}
}
Run Code Online (Sandbox Code Playgroud)

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

  • 这两种方法具有相同的名称,id
  • D.id(String)可供D访问
  • D.id(Object)的签名不是C.id(String)的签名
  • 这两种方法具有相同的擦除

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

 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) {...}
 }
Run Code Online (Sandbox Code Playgroud)

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

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

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

子签名的概念旨在表达两种方法之间的关系,这两种方法的签名不相同,但可以覆盖另一种方法.具体来说,它允许其签名不使用泛型类型的方法覆盖该方法的任何泛化版本.这很重要,因此库设计者可以独立于定义库的子类或子接口的客户端自由地生成方法.

考虑这个例子:

class CollectionConverter {
List toList(Collection c) {...}
}
class Overrider extends CollectionConverter {
 List toList(Collection c) {...}
Run Code Online (Sandbox Code Playgroud)

}

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

 class CollectionConverter {
   <T> List<T> toList(Collection<T> c) {...}
 }
Run Code Online (Sandbox Code Playgroud)

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


Tud*_*scu 5

好吧,对于第一部分,答案是Java不允许泛型方法覆盖非泛型方法,即使擦除是相同的.这意味着即使你只有覆盖方法,它也不会起作用:

 public <T extends Object> Object get(int i)
Run Code Online (Sandbox Code Playgroud)

我不知道为什么Java会造成这种限制(给它一些思考),我只是认为它与为子类化泛型类型实现的特殊情况有关.

你的第二个定义基本上转化为:

public class D extends A{

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

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

}
Run Code Online (Sandbox Code Playgroud)

这显然是个问题.