【问题标题】:Java bounded parameters in generic methods泛型方法中的 Java 有界参数
【发布时间】:2012-01-23 04:18:47
【问题描述】:

我在泛型方法中测试了一些有界参数的东西,我发现了一些奇怪的行为。
如果有人能解释以下代码 sn-p 中的两个错误,那就太好了。

假设有两个类Class1Class2 都从BaseClass 扩展而来。 Class2 实现了一个接口。

Class1 中,我有一个方法可以通过以下方式返回Class2 的实例:

public class Class2 extends BaseClass implements Interface {

    @Override
    public void method() {
        System.out.println("test"); //$NON-NLS-1$
    }
}

public class Class1 extends BaseClass {

    public <T extends BaseClass & Interface> T getTwo() {
        return new Class2();
        // Error: Type mismatch: cannot convert from Class2 to T
    }

    public static void main(String[] args) {
        Interface two = new Class1().getTwo();
        // Error: Bound mismatch: The generic method getTwo() of type Class1 is
        // not applicable for the arguments (). The inferred type Interface is
        // not a valid substitute for the bounded parameter <T extends BaseClass
        // & Interface>
        System.out.println(two);
    }
}

【问题讨论】:

  • 这似乎是对泛型的滥用。实际上getTwo 必须只有Class2BaseClassInterface 的返回类型。如果T 只为一种方法定义,那么编译器就无法知道在new Class1().getTwo() 行中哪个类完全替代了T。只有getTwo 有这种类型的输入参数才有可能。

标签: java generics methods


【解决方案1】:

出现第一个编译错误是因为方法声明的类型参数是由调用者指定的,而不是方法实现。也就是说,给定

class Class3 extends BaseClass implements Interface { ... }

调用者可以写

Class3 c3 = new Class1().<Class3>getTwo();

,但是方法实现返回一个Class2,它不是T = Class3的子类型。

第二个编译错误发生是因为调用者没有明确指定的类型参数是从方法参数和方法返回值分配给的变量的类型推断出来的。这个推论在这里失败了。 Java 语言规范推荐的通常解决方法是在这种情况下显式指定类型参数(类型推断是为了方便简单的情况;它并不旨在涵盖所有情况)。

至于如何正确声明此类型参数,我需要知道您要通过这些声明来完成什么。

【讨论】:

  • +1 好答案。我认为在你的第二个代码示例中语法是new Class1().&lt;Class3&gt;getTwo()
  • 我看到了第一个编译错误的意义,但我不太确定第二个。在我的系统上调用BaseClass two = new Class1().getTwo(); 似乎没问题,但在同事的系统上失败了,在我的系统上调用Interface two = new Class1().getTwo(); 也失败了(如上所示)......
  • 好吧,您是否尝试过明确指定类型参数,类似于我的 Class3 示例?或者通过声明Class2 two = new Class1().getTwo(); 来调整类型推断?我无法立即解释为什么它在您的系统上的行为会有所不同。您是否尝试过使用相同编译器和相同设置的完全相同的示例程序?
  • 想象“gettwo()”方法是接口的一部分,而您不知道 getTwo 的确切返回类型,您想以通用方式使用返回的对象。你知道为什么'BaseClass two = class1.getTwo();'没问题,'接口二 = class1.getTwo();'无法编译?
  • 好吧,我想是的。我可能会定义abstract class BaseClassWithInterface extends BaseClass implements Interface {} 并需要Class2 来扩展它。然后任何调用者都可以使用变量BaseClassWithInterface 来存储对象。关于你为什么的问题:可能是因为Java语言规范的算法specified in section 15.12.2.7是这样说的。
【解决方案2】:

当你知道它是Class2 时,为什么要为方法getTwo 使用泛型?只需这样做:

public Class2 getTwo() {
    return new Class2();
}

如果您要覆盖方法 public &lt;T extends BaseClass &amp; Interface&gt; T getTwo(),当您的 impl 的 TClass2 时,编译器将允许您将您的 impl 声明为 public Class2 getTwo()

【讨论】:

  • 在那个覆盖上没那么快。在您的场景中,您将覆盖一个方法,该方法可以返回满足约束的任何 T 并返回 Class2,这应该被禁止,原因与不允许 OP 的代码相同。我不确定 Java 甚至可以用存在量词表达返回类型。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多