【问题标题】:Java: Polymorphic Return Type in an Abstract Method?Java:抽象方法中的多态返回类型?
【发布时间】:2012-05-09 22:19:39
【问题描述】:

我在一个抽象 java 类中有以下代码:

protected abstract <E extends HasText & IsWidget> E createNewDisplayWidget();

编译得很好。但是,如果我在任何地方调用它,编译器都会抱怨:

Bound mismatch: The generic method createNewDisplayWidget() of type DemoClass is not applicable for the arguments (). The inferred type HasText is not a valid substitute for the bounded parameter <E extends HasText & IsWidget>

有没有办法要求抽象方法返回应该实现多个接口的东西?

注意:不,我不能创建一个特殊的接口来实现我喜欢的两个。 GWT 有像 Label 这样的小部件,它们已经实现了上述接口,我想使用上述小部件。

编辑:我从这里得到了这样做的想法(第 22 页):

http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf

【问题讨论】:

  • 你试过用逗号代替and吗?
  • 逗号表示边界在 IsWidget 处结束。 HasText 然后被忽略。编译器会抛出一个警告,说第二种类型是“隐藏的”。
  • 您能向我们展示您的一个/一些呼叫站点吗?
  • @McTrafik 好的,很高兴知道:)
  • 我不相信这实际上是可能的。返回该交叉点中的实际类。

标签: java gwt parametric-polymorphism


【解决方案1】:

我已经根据您的问题进行了尝试,并且能够顺利通过。请检查我创建的类。

TestClass

public abstract class TestClass {

    protected abstract <E extends HasText & IsWidget > E createNewDisplayWidget();

}

HasText 类

public class HasText {

}

IsWidget

public interface IsWidget {

}

DemoClass

 public class DemoClass extends HasText implements IsWidget{

    }

TestClass1

public class TestClass1 extends TestClass{

    @Override
    protected DemoClass createNewDisplayWidget() {
        // TODO Auto-generated method stub
        DemoClass type = new DemoClass();
        return type;
    }

    public void checkOut(){
        if(createNewDisplayWidget() instanceof HasText || createNewDisplayWidget() instanceof IsWidget){
            System.out.println("Yes it works");
        }
        else{
            System.out.println("It doesnt");
        }
    }

    public static void main(String[] args){
        TestClass1 check = new TestClass1();
        check.checkOut();

    }

}

当我运行我的主程序时,我总是得到“是的,它可以工作”。请让我知道我是否遗漏了什么。

【讨论】:

  • 您从给它一个更具体的返回类型的同一个类中调用createNewDisplayWidget()。我尝试从DemoClass 调用它,但无法重现 OP 的错误。也许这是 GWT 特有的问题。
  • 是的。我试图解决的问题是不必创建另一种类型,如“DemoClass”。这实际上是我采用的解决方案,但这不是我想要的,因为现在我必须为我想使用的 GWT 中的每个类创建自定义类型。
【解决方案2】:

问题出在 Eclipse 中。我从 3.7 升级到 3.7.2,编译器错误消失了。

我不知道这有什么影响的细节。如果有人有线索,请随时更新我的​​答案。

【讨论】:

【解决方案3】:

所以我尝试编写完整的代码来生成错误,但由于某种原因它没有给出错误。稍后会研究一下:

import java.util.LinkedList;

public abstract class DemoClass {

    public static void main(String[] args) {
        DemoClass dc = new DemoClassImpl();
        dc.createNewLivingAndDying().live();
        dc.killAll();
    }

    LinkedList<Dies> dying = new LinkedList<Dies>();

    public Lives createNewLivingAndDying() {
        Lives ab = newLivingAndDying(); // This is where I expected an error
        dying.add((Dies) ab);
        return ab;
    }

    public void killAll() {
        for (Dies dead : dying)
            dead.die();
    }

    protected abstract <E extends Lives & Dies> E newLivingAndDying();

}

class DemoClassImpl extends DemoClass {

    @SuppressWarnings("unchecked")
    @Override
    protected <E extends Lives & Dies> E newLivingAndDying() {
        return (E) new SomePrivateClass(); // This is what I don't understand
    }

}

interface Lives {
    public void live();
};

interface Dies { 
    public void die();
};

class SomePrivateClass implements Lives, Dies {

    @Override
    public void die() {
        System.out.println("Object Dies");
    }

    @Override
    public void live() {
        System.out.println("Object Lives");
    }
}

此代码在我的家用计算机上编译并运行良好,但在我的工作计算机上给出错误。

Bound mismatch: The generic method newLivingAndDying() of type DemoClass is not applicable for the arguments (). The inferred type Lives is not a valid substitute for the bounded parameter <E extends Lives & Dies>

在这一点上,我认为这是一个项目设置问题,但我不知道它是什么。两者都是 JRE 1.6。

【讨论】:

  • 换句话说,您通过使用Object(隐式)作为类型参数绕过错误,然后转换为任一接口。换句话说,如果你只需要一个 Lives 某处,Lives lives = newLivingAndDying() 将无法编译,但 Lives lives = (Lives) this.&lt;Object&gt;newLivingAndDying() 将编译(并且工作!)
  • @ThomasBroyer &lt;Object&gt;newLivingAndDying() 不会编译:) Object ab = newLivingAndDying(); 编译的原因很复杂,可以说是由于类型推断的怪癖。
  • @McTrafik,通过强制转换,无论声明的返回类型如何,实现都可以返回任何内容。很好,但是您会丢失编译器类型检查。你也可以声明Object newLivingAndDying()
  • @irreputable 我正在编写一个库,我的目标是不让开发人员在不实现适当接口或使用类的情况下实现该方法。也许我应该更清楚一点。
  • @ThomasBroyer Eum,没有。我不必使用Object。我可以很容易地说 Lives ab = newLivingAndDying(); 没有任何演员表。
【解决方案4】:

你想做的只有Java直接支持拦截类型,那么你就可以了

   HasText&IsWidget createNewDisplayWidget();

任何实现都必须返回一个对象,该对象是HasText&amp;IsWidget 类型的子类型;换句话说,返回的类型必须是HasTextIsWidget的子类型

不幸的是,Java 不支持这一点。

您尝试解决的问题可以从两个角度来理解:

1) 对类型变量的约束是对调用者的约束;调用者提供实际的类型参数,它必须满足约束。你真正想要的是对被调用者的约束。

2) 如果类型变量只出现在返回类型中,而不出现在方法参数类型中,则通常是有问题的迹象。在Java中,由于邪恶的类型擦除,方法体无法知道运行时类型参数,因此它无法返回调用者所需的正确类型的值,除了一些琐碎的值,如null;另一个平凡返回值的经典例子是

java.util.Collections  
    <T> Set<T> emptySet()

【讨论】:

  • 答案不正确,不应该接受。正确的是,这样的方法签名可能是问题的征兆。但是对于创建一组特定子类型的工厂,这可能没问题(为了方便起见),它可以在 Java 中工作(如果你愿意,只需添加`@SuppressWarnings("unchecked")。你可能会在运行时得到 ClassCastExceptions,它可能并不总是最好的方法,但是 这并不能解释您遇到的编译器错误
  • 对我来说In Java, due to the evil type erasure, the method body cannot know the runtime type argument, therefore it cannot return a value of proper type desired by caller 是有道理的。我尝试在签名中添加E 类型的参数,编译器停止抱怨。当然,然后该方法变得无法使用。
猜你喜欢
  • 2021-12-15
  • 1970-01-01
  • 2013-04-16
  • 2010-12-13
  • 2012-12-03
  • 2013-08-19
  • 2012-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多