Java泛型:非法的前向引用

时间:2011-01-07 17:52:16

标签: java generics syntax-error

给定通用接口

interface Foo<A, B> { }

我想编写一个实现,要求A成为B的子类。所以我想做

class Bar<A, B super A> implements Foo<A, B> { }
// --> Syntax error

class Bar<A extends B, B> implements Foo<A, B> { }
// --> illegal forward reference

但似乎有效的唯一解决方案是:

class Bar<B, A extends B> implements Foo<A, B> { }

这有点难看,因为它颠倒了通用参数的顺序 这个问题有什么解决方案或解决方法吗?

3 个答案:

答案 0 :(得分:5)

由于在Java中无法做到这一点,请尝试区别Bar<B, A extends B>

当您为Bar声明变量时,首先指定父类,然后指定子类。这就是Bar的工作原理。不要认为它是倒退 - 认为它是前锋。父母应该自然地在孩子面前指定。您添加的这个附加关系是驱动参数顺序的因素,而不是底层接口。

答案 1 :(得分:1)

在看到这个问题之后,我花了一些时间尝试了一些我认为可行的不同技术。例如,构建一个通用接口ISuper<B,A extends B>然后使用Bar<A,B> implements ISuper<B,A>(和一个类似的技术与子类和扩展而不是实现),但这只会导致类型错误,Bar.java:1: type parameter A is not within its bound 。同样,我尝试创建方法private <A extends B> Bar<A,B> foo() { return this; };并从构造函数中调用它,但这只会产生有趣的类型错误消息Bar.java:2: incompatible types found : Bar<A,B> required: Bar<A,B>

所以,我认为,不幸的是,答案是否定的。显然,这不是你所希望的答案,但似乎正确的答案是,这是不可能的。

答案 2 :(得分:1)

已经指出既没有解决方案也没有很好的解决方法。这是我最终做的。它只适用于我的特殊情况,但如果你遇到类似的问题,你可以把它作为灵感。 (这也解释了我遇到这个问题的原因)

首先,有这个类(只显示相关的界面):

class Pipe<Input, Output> {

    boolean hasNext();

    Input getNext();

    void setNext(Output o);

}

Foo接口实际上是

interface Processor<Input, Output> {

    process(Pipe<Input, Output> p);

}

并且课程Bar应该像这样工作

class JustCopyIt<Input, Output> implements Processor<Input, Output> {

    process(Pipe<Input, Output> p) {
       while (p.hasNext()) p.setNext(p.getNext());
    }

}

最简单的方法是投射如下的值:p.setNext((Output) p.getNext())。 但这很糟糕,因为它允许创建JustCopyIt<Integer, String>的实例。调用此对象会在某些时候神秘地失败,但不会在发生实际错误的位置失败。

执行class JustCopyIt<Type> implements Processor<Type, Type>也不起作用,因为我无法处理Pipe<String, Object>

所以我最终做的是将界面更改为:

interface Processor<Input, Output> {

    process(Pipe<? extends Input, ? super Output> p);

}

这样,JustCopyIt<List>就可以处理Pipe<ArrayList, Collection>

虽然这在技术上似乎是唯一有效的解决方案,但它仍然很糟糕,因为它1)仅适用于这种特殊情况,2)要求我更改接口(这并不总是可行)和3)制作代码其他处理器丑陋。

修改
阅读Keiths的回答再次启发了我的另一个解决方案:

public abstract class Bar<A, B> implements Foo<A, B> {

    public static <B, A extends B> Bar<A, B> newInstance() {
        return new BarImpl<B, A>();
    }

    private static class BarImpl<B, A extends B> extends Bar<A, B> {
        // code goes here
    }

}

// clean code without visible reversed parameters
Bar<Integer, Object> bar1 = Bar.newInstance();
Bar<Object, Integer> bar2 = Bar.newInstance(); // <- compile error