In Guava, why exactly is a โ€œTโ€ used where a โ€œsuper Tโ€ would be possible?

Why do factory utility methods often use a specific generic parameter (for example, T ) instead of a limited substitution parameter (for example ? super T )?

For example, the signature of the Function # forPredicate :

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

Why not use:

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

What could do something like the following?

 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> 

Is it because Function and Predicate consumers are expected to have the necessary limited wildcard parameters to make this unnecessary? For example, the general restrictions on Iterables # find allow you to use Predicate<Number> on Iterable<Integer> :

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

Are there any other reasons?

+8
java generics bounded-wildcard guava
source share
2 answers

Yes, itโ€™s absolutely certain that we expect consumers to have the correct limited wildcard parameters, but a couple more additional reasons for spring:

  • In the general case, we do not extend the types of general methods until we have a specific reason. This policy has paid off several times.
  • Java type inference does not always allow more complex generators to be automatically detected, so preserving narrower generics reduces the number of users who need to explicitly specify T
+6
source share

In the find() example, T can always be inferred unambiguously.

In the example forPredicate[1]() T, you can also derive it uniquely.

In the example forPredicate[2]() there is uncertainty about what should be T If the result of the method is assigned to the target type, T can be determined from the target type. Otherwise, it will scratch your head a bit:

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

In java5 / 6, it should not compile. (well, I tested it on java 6 and it compiles, but this is probably a bug, since java 6 also compiles forPredicate(p).apply("str") )

Java 7 has improved a bit, and the new rule has dictated that T=Number . It works, but for him it is more like arbitration.


Ideally, we donโ€™t need to worry about wildcards. If I need a predicate for my integers, I must declare the Predicate<Integer> parameter. The fact that the Predicate<Number> argument is also acceptable is another story. There must be a compiler task for converting Predicate<Number> to Predicate<Integer> - we can do this without overhauling the existing type system of generated java generics, this requires a new conversion rule. You can also provide a transformation library

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

All convert() methods can be generated mechanically.


Java 8 makes things easier. We still can not do

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

but we can do

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

and

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

which is equivalent to f = forPredicate[2](pn) . In java 8, we rarely need forPredicate() , etc. To convert between functional types.

+2
source share

All Articles