我正在玩一些像编程这样的功能。还有一些嵌套很深的泛型的问题。这是我的SCCE失败,其中涉及一个抽象类:
public abstract class FooGen<IN, OUT> {
OUT fn2(IN in1, IN in2) { // clever? try at a lazy way, just call the varargs version
return fnN(in1, in2);
}
abstract OUT fnN(IN...ins); // subclasses implement this
public static void main(String[] args) {
FooGen<Number, Number> foogen = new FooGen<Number, Number>() {
@Override Number fnN(Number... numbers) {
return numbers[0];
}
};
System.out.println(foogen.fn2(1.2, 3.4));
}
}
死于
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Number;
但是,对于非抽象的FooGen,它可以正常工作:
public class FooGen<IN, OUT> {
OUT fn2(IN g1, IN g2) {
return fnN(g1, g2);
}
OUT fnN(IN...gs) {
return (OUT)gs[0];
}
public static void main(String[] args) {
FooGen<Number,Number> foogen = new FooGen<Number,Number>();
System.out.println(foogen.fn2(1.2, 3.4));
}
}
打印1.2。有想法吗?似乎Java遗忘了泛型。这推动了我的通用知识的极限。 :-)
(为回答问题而添加)
首先,感谢您的支持,也感谢Paul和Daemon的有用回答。
仍然想知道为什么它在第二版中可以用作数字,但我还是有一个见解。作为思想实验,让我们在某处添加
.doubleValue()
。你不能在代码本身中,变量是IN,而不是数字。在main()
中,它只是声明了FooGen<Number,Number>
类型,但没有地方添加代码。在版本2中,它确实不能像数字一样“工作”。正如Paul和Daemon所解释的那样,从内部看,一切都是对象,并且令人毛骨悚然地回首,我自己也很好理解。基本上,在这个复杂的示例中,我对
<Number>
声明感到兴奋和误导。不要以为我会解决。整个想法是要懒惰。 :-)为了提高效率,我创建了并行接口和代码,它们采用了原始的double(和ints),在这里,这种技巧就很好了。
最佳答案
Varargs参数是第一个和最重要的数组。因此,如果没有语法糖,您的代码将如下所示:
OUT fn2(IN in1, IN in2) {
return fnN(new IN[] {in1, in2});
}
abstract OUT fnN(IN[] ins);
由于arrays of type parameters cannot be instantiated,所以
new IN[]
除外,因为type erasure并不合法。数组需要知道其组件类型,但是IN
在运行时已被擦除到其上限Object
。不幸的是,varargs调用隐藏了此问题,并且在运行时您具有
fnN(new Object[] {in1, in2})
的等效项,而fnN
已被覆盖以采用Number[]
。但是,对于非抽象的FooGen,它可以正常工作
这是因为通过直接实例化
FooGen
,您没有覆盖fnN
。因此,它在运行时接受Object[]
且没有ClassCastException
发生。例如,即使
FooGen
不是abstract
,此操作也会失败:FooGen<Number, Number> foogen = new FooGen<Number, Number>() {
@Override
Number fnN(Number... gs) {
return super.fnN(gs);
}
};
System.out.println(foogen.fn2(1.2, 3.4));
因此,您可以看到它实际上与
FooGen
的抽象性无关,但与fnN
是否被狭窄的参数类型覆盖无关。解决方案
没有简单的解决方法。一种想法是让
fnN
改为List<? extends IN>
:OUT fn2(IN in1, IN in2) {
//safe because the array won't be exposed outside the list
@SuppressWarnings("unchecked")
final List<IN> ins = Arrays.asList(in1, in2);
return fnN(ins);
}
abstract OUT fnN(List<? extends IN> ins);
如果要保留varargs支持,则可以将此方法视为实现细节并委托给它:
abstract OUT fnNImpl(List<? extends IN> ins);
public final OUT fnN(IN... ins) {
return fnNImpl(Arrays.asList(ins));
}