在番石榴中,为什么只用“T”就可以使用“?super T”?

时间:2013-01-31 17:11:19

标签: java generics guava bounded-wildcard

为什么实用工厂方法经常使用特定的通用参数(如T)而不是有界通配符参数(如? super T)?

例如,Functions#forPredicate的签名是:

public static <T> Function<T, Boolean> forPredicate(Predicate<T> predicate)

为什么不使用:

public static <T> Function<T, Boolean> forPredicate(Predicate<? super T> predicate)

哪种可能会产生以下类似的东西?

Predicate<Number> isPositivePredicate = ...
Function<Integer, Boolean> isPositiveInteger = Functions.forPredicate(isPositivePredicate);
// above line is compiler error:
//   Type mismatch: cannot convert from Function<Number,Boolean> to Function<Integer,Boolean>

是否因为FunctionPredicate使用者需要具有必要的有界通配符参数才能使其不必要?例如,Iterables#find上的通用边界将允许在Predicate<Number>上使用Iterable<Integer>

public static <T> T find(Iterable<T> iterable,
                         Predicate<? super T> predicate)

还有其他原因吗?

2 个答案:

答案 0 :(得分:6)

是的,绝对准确的是我们希望消费者拥有正确的有界通配符参数,但还有一些其他原因让人想起:

  • 一般来说,在我们有特定原因之前,我们不会扩大泛型方法的类型。这项政策已多次获得回报。
  • Java的类型推断并不总是自动计算出更高级的泛型,因此保持较窄的泛型会减少需要明确指定T的用户数。

答案 1 :(得分:2)

find()示例中,T始终可以毫不含糊地推断出来。

forPredicate[1]()示例中,T也可以明确推断。

forPredicate[2]()示例中,T应该是不确定的。如果将方法的结果分配给目标类型,则可以从目标类型确定T。否则它有点头疼:

forPredicate(isPositive).apply(42);  // T is a subtype of Number, but which one?

在java5 / 6中,它不应该编译。 (我在java 6上测试它并且它确实编译了,但它可能是一个bug,因为java 6也编译forPredicate(p).apply("str")

Java 7改进了一点,新规则恰好指示T=Number。它有效,但感觉更像是仲裁。


理想情况下,我们不需要担心通配符。如果我需要一个整数的谓词,我应该声明一个Predicate<Integer>参数。另一个故事是Predicate<Number>论证也可以接受的事实。将Predicate<Number>转换为Predicate<Integer>应该是编译器的工作 - 我们可以在不修改现有java泛型类型系统的情况下完成它,只需要一个新的转换规则。还可以提供转换库

Predicate<Number>  pn = ...;
Predicate<Integer> pi = convert(pn);

Iterable<Integer> iter1 = ...;
Iterable<Number>  iter2 = convert(iter1);

所有convert()方法都可以机械生成。


Java 8使事情变得容易一些。我们仍然无法做到

Predicate<Number>  pn = n -> n.intValue()>0;
Predicate<Integer> pi = pn;  // error!

但我们可以做到

Predicate<Integer> pi = pn::test;  // ok
// it means        pi = (Integer i)->pn.test(i)

Function<Integer, Boolean> f = pn::test;   // ok

相当于f = forPredicate[2](pn)。在java 8中,我们很少需要forPredicate()等来在功能类型之间进行转换。