【问题标题】:Overriding method with generic return type具有泛型返回类型的覆盖方法
【发布时间】:2011-02-28 16:48:57
【问题描述】:

假设我有一个定义以下抽象方法的超类

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

现在如果我想在某个子类中覆盖它

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

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

类型安全:getMainClass() 类型 SubFoo 的返回类型 Class&lt;Implementation&gt; 需要未经检查的转换才能符合 Class&lt;Interface&gt; 类型 SuperFoo 的要求

如果&lt;T extends Interface&gt;Class&lt;Implementation&gt; 不属于Class&lt;T&gt; 吗?有什么方法可以正确消除警告吗?

【问题讨论】:

    标签: java generics


    【解决方案1】:

    覆盖方法的返回类型必须是被覆盖方法返回类型的子类型。

    Class&lt;Impl&gt; 不是Class&lt;T&gt; 的子类型,其中&lt;T extends Interface&gt;。 T在这里是未知的。

    根据子类型规则,Class&lt;Impl&gt;Class&lt;? extends Interface&gt; 的子类型。


    一些关于通配符的子类型规则:

    对于任何类型的 X

    • A&lt;X&gt;A&lt;? extends X&gt; 的子类型

    • A&lt;X&gt;A&lt;? super X&gt; 的子类型

    如果ST 的子类型

    • A&lt;? extends S&gt;A&lt;? extends T&gt; 的子类型

    • A&lt;? super T&gt;A&lt;? super S&gt; 的子类型

    更简洁地说,(&lt;: 表示“是”的子类型)

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

    【讨论】:

    • 这完全解决了问题。谢谢你。在旁注中,对于这类关于泛型的问题,什么是一个很好的参考,例如解释子类型规则?
    • Angelika Langer's generics FAQ - 它几乎涵盖了所有边缘情况,但每个部分都(相对)容易理解。
    • @alh84001 不幸的是,我不知道比 Java 语言规范更好的来源。
    【解决方案2】:

    考虑以下与您类似的场景:

    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&lt;...&gt; 时,这不是问题(据我了解),因此您可以合法地禁止警告:

    @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 的回答。

    【讨论】:

    • 感谢您的澄清。我想我会保持原样。
    • 嗯..我认为你的解决方案更好)
    【解决方案3】:

    试试这个

    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 提到的例子被破坏了。我重新组织了它。

    【讨论】:

    • 不会因为类型擦除而导致异常。
    • 谢谢你的例子。它使事情更清楚。不幸的是,我在使用您的建议时收到了同样的警告,但这是我所期望的。我想我会保持原样。
    【解决方案4】:

    你为什么想要public abstract Class&lt;? extends Interface&gt; 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());
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-07-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-27
      • 2020-03-14
      相关资源
      最近更新 更多