Is the Java compiler able to infer the type of a generic static function from its context as the argument to another generic static function?
For example, I have a simple Pair class:
public class Pair<F, S> {
private final F mFirst;
private final S mSecond;
public Pair(F first, S second) {
mFirst = checkNotNull(first);
mSecond = checkNotNull(second);
}
public static <F, S, F1 extends F, S1 extends S> Pair<F, S> of(F1 first, S1 second) {
return new Pair<F, S>(first, second);
}
public F first() {
return mFirst;
}
public S second() {
return mSecond;
}
// ...
}
And I have the following generic static function:
public static <F, P extends Pair<F, ?>> Function<P, F> deferredFirst() {
return (Function<P, F>)DEFERRED_FIRST;
}
private static final Function<Pair<Object, ?>, Object> DEFERRED_FIRST =
new Function<Pair<Object,?>, Object>() {
@Override
public Object apply(Pair<Object, ?> input) {
return input.first();
}
};
Which I wish to use as follows (Collections2.transform is from Google Guava):
List<Pair<Integer, Double>> values = ...
Collection<Integer> firsts = Collections2.transform(values,
Pair.deferredFirst());
To which the compiler complains:
The method transform(Collection<F>, Function<? super F,T>) in the type
Collections2 is not applicable for the arguments
(List<Pair<Integer,Double>>, Function<Pair<Object,?>,Object>)
So it seems that the compiler fails to propagate the types inferred for transform() to deferredFirst() as it thinks they are Objects.
Forcing the compiler to understand the types in either of these ways works:
Function<Pair<Integer, ?>, Integer> func = Pair.deferredFirst();
Collection<Integer> firsts = Collections2.transform(values, func);
Collection<Integer> firsts = Collections2.transform(values,
Pair.<Integer, Pair<Integer, ?>>deferredFirst());
Is it possible to change either function’s signature to allow the compiler to infer/propagate the types?
Edit: For Bohemian, here’s a possible method the above example could be used in:
public static int sumSomeInts(List<Pair<Integer, Double>> values) {
Collection<Integer> ints = Collections2.transform(values,
Pair.deferredFirst());
int sum = 0;
for(int i : ints)
sum += i;
return sum;
}
Type inference is nasty and complicated. They have to stop somewhere. Consider
In the assignment context, the intention of the programmer is clear,
Tshould beStringIn the next line, not so much.
The
printmethod is not a very fair example, it is heavily overloaded. Supposeprintisn’t overloaded, its parameter type is fixed, soTcan be clearly inferred. Shouldn’t the compiler be smart enough to figure it out?That sounds reasonable, until one ventures to read the related spec text, 15.12 Method Invocation Expressions Good luck changing anything in that mess!
It is so complicated, not even the compiler authors understand it. There are tons of bugs in javac and other compilers that originated from this section of the spec.