给定通用接口
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> { }
这有点难看,因为它颠倒了通用参数的顺序 这个问题有什么解决方案或解决方法吗?
答案 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