【问题标题】:Overriding generic methods from base class覆盖基类的泛型方法
【发布时间】:2021-08-27 08:16:55
【问题描述】:

我有一个定义如下的类层次结构:

class ClassA{}

class ClassB extends ClassA{}

class BaseClass
{
    public <T extends ClassA> T method1(){ return null; }

    public <T extends ClassA> T method2(T param1){ return null; }
}

我想扩展上面的 BaseClass 并覆盖 method1method2 的实现,但我在 DerivedClass 中的方法签名上苦苦挣扎。这是我尝试过的:

class DerivedClass extends BaseClass
{
    @Override
    public ClassB method1() {return new ClassB();}

    @Override
    public ClassB method2(ClassB param1) {return new ClassB();}
}

对于method1,我收到警告:

Type safety: The return type ClassB for method1() from the type DerivedClass needs unchecked conversion to conform to T from the type BaseClass

对于method2,我收到以下错误:

The method method2(ClassB) of type DerivedClass must override or implement a supertype method

我该如何解决这个问题?

编辑:我不得不重构设计并将泛型类型放在BaseClass 而不是每个单独的方法上,这样就不会发生这种情况。

【问题讨论】:

  • 这两个答案似乎都解决了您遇到的错误以及如何解决这些错误的问题。您应该编辑问题以包含更多上下文,以便我们了解为什么这些答案不适合您的情况。
  • @matt 更新了问题。

标签: java generics


【解决方案1】:

第一个警告是因为 JVM,由于type erasure 无法知道ClassB 是否正确(它是否扩展了ClassA),并且因为存在抛出某些异常的风险,所以它会警告您。要解决此问题,您可以在方法上添加 SuppressWarnings 注释

@Override
@SuppressWarnings("unchecked")
public ClassB method1() {return new ClassB();}

第二件事很相似,但现在JVM无法创建正确的type/method signature(请注意返回类型不包含在Java中的签名中!还请注意public ClassA method2(ClassA param1)方法不会导致这样的错误)。

要解决这个问题,您需要让编译器确保被覆盖的方法具有正确的签名,为此您需要在类级别移动泛型类型声明

class BaseClass <T extends ClassA> 

然后用适当的类型扩展这个类

class DerivedClass extends BaseClass<ClassB> {
    @Override
    public ClassB method1() { // notice that you don't need Suppress annotation anymore
        return new ClassB();
    }

    @Override
    public ClassB method2(ClassB param1) {
        return new ClassB();
    }
}

现在编译器可以检查使用的类型是否在绑定中

class ClassC {}
class DerivedClass extends BaseClass<ClassC> {
// ERROR: Type parameter 'com.mantkowicz.example.ClassC' is not within its bound

在这里阅读更多:

【讨论】:

  • 为了拥有一个 BaseClass 实例,我在方法上应用了泛型参数。如果我将泛型参数放在基类上,那么我需要为每种类型创建单独的实例。主要动机是避免为每种类型创建这些单独的实例。
  • @JavaLearner 你能说明这个问题吗?目前还不清楚为什么你需要泛型。你可以作为A类返回。然后你的两种方法都有效。
  • @JavaLearner 然后只创建一个不带@Overridepublic ClassB method2(ClassB param1) 并在必要时调用超级方法,例如return (ClassB) super.method2(param1);。您目前正在尝试做的不是正确使用继承,Java 编译器不会让您这样做
  • @matt 我在所有这些类之外都有一个带有签名void callMethods(BaseClass b); 的方法。它所做的只是在参数b 上调用methodAmethodB。在大多数情况下,我只想使用BaseClass,但在某些情况下,我想覆盖它并使用DerivedClass。可以有多个这样的 DerivedClasses。如果我继续让 BaseClass 接受一个通用参数,那么每次调用 callMethods 都需要创建一个单独的 BaseClass 对象,并附加给定类型,我想避免这种情况。
  • "...需要创建一个单独的 BaseClass 对象,并附加给定的类型,"这不正是您正在做的吗? “...但是在某些情况下我想覆盖它并使用 DerivedClass” 派生类不会是一个单独的实例吗?
【解决方案2】:

这个方法行得通。

public class BaseClass<T extends ClassA> {
    public T method1() {
        return null;
    }

    public T method2(T param1) {
        return null;
    }
}
public class DerivedClass extends BaseClass<ClassB> {
    @Override
    public ClassB method1() {
        return new ClassB();
    }

    @Override
    public ClassB method2(ClassB param1) {
        return new ClassB();
    }
}

【讨论】:

  • 你没看错:这行得通。但这根本不能解释有什么不同或为什么会更好。
  • 是的,我理解,但为了拥有一个 BaseClass 实例,我在方法上应用了泛型参数。如果我将泛型参数放在基类上,那么我需要为每种类型创建单独的实例。主要动机是避免为每种类型创建这些单独的实例。
猜你喜欢
  • 2020-03-14
  • 2012-03-01
  • 2013-08-20
  • 1970-01-01
  • 2019-08-12
  • 2012-04-25
  • 2015-06-24
  • 2016-11-17
  • 1970-01-01
相关资源
最近更新 更多