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 一个函数的两种不同类型,并且 CD 是不同的类型。

有没有什么办法可以编译上面的代码?

你对类型变量有根本性的误解。当你声明一个像

这样的方法时
public static <T extends A & B> T f(boolean b) { … }

您声明了一个 类型变量 T调用者 可以将实际类型(或类型变量调用者的上下文)。例如,调用者可以执行以下操作:

class SomethingCompletelyUnknownToF implements A,B {}

SomethingCompletelyUnknownToF var = f(trueOrFalse);

编译器将接受,因为 f 的调用者用于 T 的类型 SomethingCompletelyUnknownToF 满足类型必须实现 AB。当然,这会在运行时失败,因为 CD 都不能分配给 SomethingCompletelyUnknownToF。事实上,f 不可能实现这种 return 甚至不知道的特定类型的期望。

总而言之,类型变量不是方法可以分配类型的变量,类型变量是调用者选择的类型的占位符.

所以方法签名

public static <T extends A & B> void g(T t) { … }

更有意义,因为调用者为 T 选择的任何实际类型,它将满足方法的期望,即在作为参数传递时实现 AB。当然,g 不能指望它是 DC,因为它可能是实现 AB 的完全未知的类型。

也就是说,在 Java 中无法表达 return 类型扩展了两种类型(除了声明具体类型扩展两种类型)。在 RandomAccess 的情况下,无论如何都不需要麻烦,因为它没有任何后果。请注意,JRE 类 也从不声明何时保证 returned List 实现此标记接口。这是通过从不期望它作为参数类型来适应的。同样,Serializable 永远不会在任何地方声明为 return 或参数类型。

我同意 Holger 在 中所说的一切。但我想补充一件事,因为棘手的类型非常有趣。

我相信你喜欢做的是有一个交叉路口 return类型的f,像这样:

public static A & B f(boolean b)

然后 f 将 return 一些同时实现 AB 的对象。这将使方法类型检查的 return 语句,您可以将其 return 值分配给 AB 变量。

但这当然是不允许的,Java 中的交集类型只允许在类型变量上使用。

你可以试试这个:

public static Optional<? extends A & B> f(boolean b)

这里交集类型是可以的,因为它实例化了Optional的类型参数。但这无论如何是不允许的,因为通配符只能有一个界限,不能使用&


Ceylon 是一种非常有趣的编程语言,它支持 first-class 交集和联合类型。你可以用 Ceylon 写上面的例子。

看看,真的有趣!