我遇到了Java编译器的一个完全奇怪的行为。
循环泛型类型时,我无法将超类型转换为子类型 涉及

JUnit测试用例重现问题:

public class _SupertypeGenericTest {

    interface ISpace<S extends ISpace<S, A>, A extends IAtom<S, A>> {
    }

    interface IAtom<S extends ISpace<S, A>, A extends IAtom<S, A>> {
    }

    static class Space
            implements ISpace<Space, Atom> {
    }

    static class Atom
            implements IAtom<Space, Atom> {
    }

    public void test() {
        ISpace<?, ?> spaceSupertype = new Space();
        IAtom<?, ?> atomSupertype = new Atom();

        Space space = (Space) spaceSupertype;  // cast error
        Atom atom = (Atom) atomSupertype;  // cast error
    }
}

编译错误输出:

_SupertypeGenericTest.java:33: inconvertible types
found   : pinetag.data._SupertypeGenericTest.ISpace<capture#341 of ?,capture#820 of ?>
required: pinetag.data._SupertypeGenericTest.Space
                Space space = (Space) spaceSupertype;
                                    ^

_SupertypeGenericTest.java:34: inconvertible types
found   : pinetag.data._SupertypeGenericTest.IAtom<capture#94 of ?,capture#48 of ?>
required: pinetag.data._SupertypeGenericTest.Atom
                Atom atom = (Atom) atomSupertype;
                                ^
2 errors

注意:我正在使用Netbeans最新的trunk,捆绑的Ant,最新的Java 6版本。
我尝试从命令行使用Ant(Netbeans生成build.xml文件) 但它会导致相同的错误。

有什么问题?
是否有一种优雅的方式来解决问题?

奇怪的是:Netbeans没有标记错误(甚至没有警告) 在给定的代码中。

修改结果 不,现在我理解没有
Eclipse 3.4.1既没有标记警告也没有标记错误和编译 代码没有问题!!!
怎么会这样?我想,从命令行使用Ant Netbeans提供的build.xml将是中立的。
我错过了什么吗?

编辑2:
使用 JDK7 库和JDK7代码格式,netbeans无需编译 错误/警告!点击 (我使用的是1.7.0-ea-b55)

编辑3:
更改标题以表明我们正在处理javac错误。

有帮助吗?

解决方案

我并不声称能够轻松理解那些复杂的泛型类型,但如果您发现某些代码在javac中编译而在ecj(eclipse编译器)中没有,那么请同时提交错误报告< a href =“http://bugs.sun.com/”rel =“nofollow noreferrer”> Sun 和 Eclipse 并描述清楚的情况(如果你还提到你提交了两个错误报告并提到了他们各自的URL,那就最好了,尽管对于Sun来说可能需要一段时间才能公开访问bug。)

我过去做过那件事并得到了很好的回应

  1. 其中一个团队找出了正确的方法(给出编译错误,警告或什么都没有)
  2. 并修复了错误的编译器
  3. 由于两个编译器都实现了相同的规范,如果其中只有一个编译代码,其中一个定义错误。

    记录:

    我尝试使用.class(javac 1.6.0_13)和<=>(Eclipse Java编译器0.894_R34x,3.4.2版本)编译示例代码并且<=>大声抱怨并且未能生成任何<=>文件,而<=>仅抱怨一些未使用的变量(警告)并生成所有预期的<=>文件。

其他提示

我最终使用了非泛型:

    @Test
    public void test() {
            ISpace spaceSupertype = new Space();
            IAtom atomSupertype = new Atom();

            Space space = (Space) spaceSupertype;  // ok
            Atom atom = (Atom) atomSupertype;  // ok
    }

是什么阻碍了你使用没有通配符的类型?

public void test() {
    ISpace<Space, Atom> spaceSupertype = new Space();
    IAtom<Space, Atom> atomSupertype = new Atom();

    Space space = (Space) spaceSupertype;  // no error
    Atom atom = (Atom) atomSupertype;  // no error
}

这样它读得更清楚,加上它编译并运行:) 我认为这将是<!>“一种优雅的方式解决问题<!>”;

问题可能在于您尝试将IAtom<?, ?>转换为Atom(这是Atom<Atom, Space>)。系统应该知道?可能是Atom和Space是不是很糟糕?

如果你不知道要填充泛型的地方的类型,你通常只是放弃整个事情,如

ISpace spaceSupertype = new Space();

生成编译器警告(而不是错误),但您的代码仍将执行(但如果实际类型不兼容,则会出现运行时错误)。

然而,这整件事看起来没有意义。你说你需要强力打字,然后你坚持IAtom类型去哪里。然后你转过身来试图施展它们。

如果你需要能够将它们转换为Space和Atom,你可能应该只使用它们开始。如果你不能,因为你最终会在这些变量中加入其他类型的代码,那么当你改变运行时类型时,你的代码就会破坏,除非你使用一堆if / then语句(比如在本评论的结尾)。

但是,真的,如果你做的很奇怪,我认为我们正在寻找糟糕的代码设计。重新思考你是如何构建这个的。也许您需要其他类或接口来实现您在此处获得的功能。

问问自己,<!>“我真的需要强大的类型吗?它有什么收获?<!>最好不要添加字段,因为你使用的是接口。 (请记住,如果仅通过方法访问字段,那么您只是向公共接口添加方法。)如果它添加方法,那么使用单个接口ISpacetest()这是一个坏主意,因为无论如何,你只能使用IAtom2与该子类型。 instanceof不会推广到<=> / <=>的其他实现。如果您将在此处放置的所有实现都具有<=>所需的相同方法,但并非所有<=> / <=>的实现都具有它们,则需要一个中间子接口:

public interface IAtom2 extends IAtom
{
    [additional methods]
}

然后您可以在<=>中使用<=>代替<=>。然后你自动得到你需要的打字,不需要泛型。请记住,如果一组类都具有公共公共接口(方法和字段集),那么公共接口是超类型或接口的良好候选者。我所描述的是平行四边形,矩形,方形关系,你试图跳过矩形部分。

如果您不打算重新设计,另一个想法就是放弃循环泛型,只需通过<=>进行实例测试:

if (spaceSupertype instanceof Space)
{
    Space space = (Space)spaceSupertype;
    ...
}
else
...
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top