覆盖带有通用返回类型的方法

10
假设我有一个超类,定义了以下抽象方法:
public abstract <T extends Interface> Class<T> getMainClass();

现在,如果我希望在某个子类中覆盖它

public Class<Implementation> getMainClass(){
    return Implementation.class;
}

我收到了有关类型安全和未经检查的转换的警告:

类型安全:从类型SubFoogetMainClass()返回类型Class<Implementation>需要未经检查的转换以符合来自类型SuperFooClass<Interface>

如果<T extends Interface>,那么Class<Implementation>不属于Class<T>吗?有没有什么方法可以正确地消除这个警告?

4个回答

14
超载方法的返回类型必须是被覆盖方法返回类型的子类型。
<T extends Interface> 时,Class<Impl> 不是 Class<T> 的子类型。在这里,T 是未知的。
按照子类型规则,Class<Impl>Class<? extends Interface> 的子类型。

一些关于通配符的子类型规则:
对于任意类型 X:
  • A<X>A<? extends X> 的子类型

  • A<X>A<? super X> 的子类型

如果 ST 的子类型:
  • A<? extends S>A<? extends T> 的子类型

  • A<? super T>A<? super S> 的子类型

更加简洁地说,(<: 表示“是子类型”)

A<S>    <:    A<? extends S>    <:    A<? extends T>

A<T>    <:    A<?  super  T>    <:    A<?  super  S>

这完全解决了问题。谢谢。顺便问一下,有没有关于泛型这类问题的好参考资料,例如子类型规则的解释? - alh84001
2
这里有Angelika Langer的泛型FAQ - 它涵盖了几乎所有边缘情况,但每个部分(相对而言)都很容易理解和掌握。 - Andrzej Doyle
@alh84001,很抱歉,我不知道比Java语言规范更好的资源。 - irreputable

3
考虑以下类似于您的场景:

假设您的情况如下:

public class SuperFoo {
     public abstract <T extends Interface> List<T> getList();
} 

public class SubFoo extends SuperFoo {
     private List<Implementation> l = new ArrayList<Implementation>();

     public List<Implementation> getList() {
          return l;
     }

     public void iterate() {
          for (Implementation i: l) ...;
     }
}

SubFoo subFoo = new SubFoo();
SuperFoo superFoo = subFoo;
superFoo.getList().add(new AnotherImplementation()); // Valid operation!
subFoo.iterate(); // Unexpected ClassCastException!

在这种情况下,未经检查的转换警告会提醒您可能会出现意外的ClassCastException。
然而,在您的情况下,当返回类型是Class<...>时,这不是问题(据我所知),因此您可以合法地抑制警告:
@SuppressWarnings("unchecked")
public Class<Implementation> getMainClass(){ ... }  

另一种选择是使SuperFoo本身成为通用的:

public class SuperFoo<T extends Interface> {
    public abstract Class<T> getMainClass(); 
}

public class SubFoo extends SuperFoo<Implementation> {
    public Class<Implementation> getMainClass() { ... }
}

如果您想了解更多选项(也许是最佳选项),请参考Stas Kurilin的答案。


谢谢您的澄清。我想我会将其保留原样。 - alh84001
嗯..我认为你的解决方案更好) - Stan Kurilin

1

试一下这个

public abstract Class<? extends Interface> getMainClass();

重新组织的示例 通过这样的警告,Java试图防止这种情况发生

class OtherImpl implements Interface{ 
} 
A a = new B();//where A - your abstract class and B - implementation
Class<OtherImpl> other = a.<OtherImpl>getMainClass();//some broken think, But _without_ runtime exception

正如@axtavt所提到的,示例已经失效。我进行了重新组织。


由于类型擦除,它不会引起异常。 - axtavt
谢谢你的示例,它让事情更清楚了。不幸的是,使用你的建议时我得到了相同的警告,但这正是我所期望的。我猜我会像以前一样离开它。 - alh84001

0
为什么你想要类似于 public abstract Class<? extends Interface> getMainClass(); 而不是 public abstract Interface getMainClass();
我认为你可以简单地返回一个Interface实例,然后,如果调用者想要访问底层运行时类,只需在返回的对象上调用getClass()
本质上,我认为你可以简单地这样做。
public InterfaceImpl implements Interface {
    // ...
};

public abstract class A {
    public abstract Interface getMainClass();
    // ...
}

public class AImpl {
    return new InterfaceImpl();
}

public class Main {
    public static void main(String[] args) {
        AImpl aImpl = new AImpl();
        Interface i = aImpl.getMainClass();
        System.out.println(i.getClass());
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接