Return 通用类型的交集
Return intersection of generic types
我想要一个函数return一个保证实现两个接口的对象。在编译时不一定知道确切的对象。我的代码看起来像:
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: error: incompatible types: C cannot be converted to T
return new C();
^
where T is a type-variable:
T extends A,B declared in method f(boolean)
HelloWorld.java:14: error: incompatible types: D cannot be converted to T
return new D();
^
where T is a type-variable:
T extends A,B declared in method f(boolean)
这不起作用,因为您不能 return 一个函数的两种不同类型,并且 C
和 D
是不同的类型。
有没有什么办法可以编译上面的代码?
你对类型变量有根本性的误解。当你声明一个像
这样的方法时
public static <T extends A & B> T f(boolean b) { … }
您声明了一个 类型变量 T
,调用者 可以将实际类型(或类型变量调用者的上下文)。例如,调用者可以执行以下操作:
class SomethingCompletelyUnknownToF implements A,B {}
SomethingCompletelyUnknownToF var = f(trueOrFalse);
编译器将接受,因为 f
的调用者用于 T
的类型 SomethingCompletelyUnknownToF
满足类型必须实现 A
或 B
。当然,这会在运行时失败,因为 C
和 D
都不能分配给 SomethingCompletelyUnknownToF
。事实上,f
不可能实现这种 return 甚至不知道的特定类型的期望。
总而言之,类型变量不是方法可以分配类型的变量,类型变量是调用者选择的类型的占位符.
所以方法签名
public static <T extends A & B> void g(T t) { … }
更有意义,因为调用者为 T
选择的任何实际类型,它将满足方法的期望,即在作为参数传递时实现 A
和 B
。当然,g
不能指望它是 D
或 C
,因为它可能是实现 A
和 B
的完全未知的类型。
也就是说,在 Java 中无法表达 return 类型扩展了两种类型(除了声明具体类型扩展两种类型)。在 RandomAccess
的情况下,无论如何都不需要麻烦,因为它没有任何后果。请注意,JRE 类 也从不声明何时保证 returned List
实现此标记接口。这是通过从不期望它作为参数类型来适应的。同样,Serializable
永远不会在任何地方声明为 return 或参数类型。
我同意 Holger 在 中所说的一切。但我想补充一件事,因为棘手的类型非常有趣。
我相信你喜欢做的是有一个交叉路口 return类型的f
,像这样:
public static A & B f(boolean b)
然后 f
将 return 一些同时实现 A
和 B
的对象。这将使方法类型检查的 return 语句,您可以将其 return 值分配给 A
或 B
变量。
但这当然是不允许的,Java 中的交集类型只允许在类型变量上使用。
你可以试试这个:
public static Optional<? extends A & B> f(boolean b)
这里交集类型是可以的,因为它实例化了Optional
的类型参数。但这无论如何是不允许的,因为通配符只能有一个界限,不能使用&
。
Ceylon 是一种非常有趣的编程语言,它支持 first-class 交集和联合类型。你可以用 Ceylon 写上面的例子。
看看,真的有趣!
我想要一个函数return一个保证实现两个接口的对象。在编译时不一定知道确切的对象。我的代码看起来像:
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: error: incompatible types: C cannot be converted to T
return new C();
^
where T is a type-variable:
T extends A,B declared in method f(boolean)
HelloWorld.java:14: error: incompatible types: D cannot be converted to T
return new D();
^
where T is a type-variable:
T extends A,B declared in method f(boolean)
这不起作用,因为您不能 return 一个函数的两种不同类型,并且 C
和 D
是不同的类型。
有没有什么办法可以编译上面的代码?
你对类型变量有根本性的误解。当你声明一个像
这样的方法时public static <T extends A & B> T f(boolean b) { … }
您声明了一个 类型变量 T
,调用者 可以将实际类型(或类型变量调用者的上下文)。例如,调用者可以执行以下操作:
class SomethingCompletelyUnknownToF implements A,B {}
SomethingCompletelyUnknownToF var = f(trueOrFalse);
编译器将接受,因为 f
的调用者用于 T
的类型 SomethingCompletelyUnknownToF
满足类型必须实现 A
或 B
。当然,这会在运行时失败,因为 C
和 D
都不能分配给 SomethingCompletelyUnknownToF
。事实上,f
不可能实现这种 return 甚至不知道的特定类型的期望。
总而言之,类型变量不是方法可以分配类型的变量,类型变量是调用者选择的类型的占位符.
所以方法签名
public static <T extends A & B> void g(T t) { … }
更有意义,因为调用者为 T
选择的任何实际类型,它将满足方法的期望,即在作为参数传递时实现 A
和 B
。当然,g
不能指望它是 D
或 C
,因为它可能是实现 A
和 B
的完全未知的类型。
也就是说,在 Java 中无法表达 return 类型扩展了两种类型(除了声明具体类型扩展两种类型)。在 RandomAccess
的情况下,无论如何都不需要麻烦,因为它没有任何后果。请注意,JRE 类 也从不声明何时保证 returned List
实现此标记接口。这是通过从不期望它作为参数类型来适应的。同样,Serializable
永远不会在任何地方声明为 return 或参数类型。
我同意 Holger 在
我相信你喜欢做的是有一个交叉路口 return类型的f
,像这样:
public static A & B f(boolean b)
然后 f
将 return 一些同时实现 A
和 B
的对象。这将使方法类型检查的 return 语句,您可以将其 return 值分配给 A
或 B
变量。
但这当然是不允许的,Java 中的交集类型只允许在类型变量上使用。
你可以试试这个:
public static Optional<? extends A & B> f(boolean b)
这里交集类型是可以的,因为它实例化了Optional
的类型参数。但这无论如何是不允许的,因为通配符只能有一个界限,不能使用&
。
Ceylon 是一种非常有趣的编程语言,它支持 first-class 交集和联合类型。你可以用 Ceylon 写上面的例子。
看看,真的有趣!