我想让函数返回一个保证实现两个接口的对象。在编译时不一定知道确切的对象。我的代码看起来像:
class HelloWorld {
public interface A {}
public interface B {}
public static class C implements A, B {}
public static class D implements A, B {}
public static <T extends A & B> void g(T t) {}
public static <T extends A & B> T f(boolean b) {
if (b)
return new C(); // Doesn't compile
return new D(); // Doesn't compile
}
public static void main(String []args){
g(f(true));
g(f(false));
<what_should_I_write_here> x = f(<user_inputted_boolean>);
}
}
尝试编译时出现以下错误:
HelloWorld.java:13:错误:不兼容的类型:C无法转换为T
中声明的A,B
返回新的C();
^
其中T是一个类型变量:
T扩展A,B在方法f(布尔值)中声明 HelloWorld.java:14:错误:不兼容的类型:D无法转换为T
返回新的D();
^
其中T是一个类型变量:
T扩展了方法f(布尔值)
这不起作用,因为您无法从函数中返回两种不同的类型,C
和D
是不同的类型。
有没有办法让上面的代码编译?
答案 0 :(得分:4)
你对类型变量有一个基本的误解。当您声明类似
的方法时public static <T extends A & B> T f(boolean b) { … }
您声明类型变量 T
调用者可以为其分配实际类型(或调用方上下文的类型变量)。例如,呼叫者可以执行以下操作:
class SomethingCompletelyUnknownToF implements A,B {}
SomethingCompletelyUnknownToF var = f(trueOrFalse);
编译器将接受的,因为SomethingCompletelyUnknownToF
调用者对T
使用的类型f
满足类型必须实现的约束A
或{{1 }}。当然,这会在运行时失败,因为B
和C
都不能分配给D
。事实上,SomethingCompletelyUnknownToF
不可能满足这种期望,即返回它甚至不知道的特定类型。
总而言之,类型变量不是方法可以分配类型的变量,类型变量是调用者选择的类型的占位符。
所以方法签名
f
与调用者为public static <T extends A & B> void g(T t) { … }
选择的实际类型更有意义,它将满足方法在作为参数传递时实现T
和A
的期望。当然,B
不能指望它是g
或D
,因为它可能是实现C
和A
的完全未知的类型。< / p>
也就是说,Java中没有办法表达返回类型扩展两种类型(除了声明扩展两者的具体类型)。在B
的情况下,无论如何都没有必要打扰,因为它没有任何后果。请注意,当返回的RandomAccess
保证实现此标记接口时,JRE类也永远不会声明。这可以通过从不期望它作为参数类型来适应。同样,List
永远不会在任何地方声明为返回或参数类型。
答案 1 :(得分:1)
我同意霍尔格在their answer中所说的一切。但是我想补充一点,因为棘手的类型非常有趣。
我相信你喜欢做的事情是让交集返回类型为f
,如下所示:
public static A & B f(boolean b)
然后f
会返回一个同时实现A
和B
的对象。这将使方法类型的返回语句检查,您可以将其返回值分配给A
或B
变量。
但当然这是不允许的,Java中的交集类型只允许在类型变量上使用。
你可以试试这个:
public static Optional<? extends A & B> f(boolean b)
这里的交集类型没问题,因为它实例化了Optional
的类型参数。但是这是不允许的,因为通配符只能有一个绑定,&
不能使用。
Ceylon 是一种非常有趣的编程语言,它支持一流的交集和联合类型。你可以在锡兰写上面的例子。
检查一下,真的有趣!