8

为什么实用程序工厂方法经常使用特定的泛型参数(如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>

是不是因为消费者Function并且Predicate被期望有必要的有界通配符参数来使这变得不必要?例如,Iterables#find上的通用边界将允许 aPredicate<Number>用于 a Iterable<Integer>

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

还有其他原因吗?

4

2 回答 2

6

是的,我们期望消费者拥有正确的有界通配符参数是绝对准确的,但我想到了另外几个原因:

  • 一般来说,除非我们有特定的理由,否则我们不会扩大泛型方法的类型。这项政策已多次获得回报。
  • Java 的类型推断并不总是能够自动找出更高级的泛型,因此保留较窄的泛型可以减少需要显式指定T.
于 2013-01-31T17:50:37.967 回答
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 上对其进行了测试,它确实可以编译,但这可能是一个错误,因为 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()etc 在函数类型之间进行转换。

于 2013-01-31T22:03:25.913 回答