Inferring generic types of nested static generic functions
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> d开发者_开发知识库eferredFirst() {
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
static <T> T foo();
String s = foo();
print( foo() )
In the assignment context, the intention of the programmer is clear, T
should be String
In the next line, not so much.
The print
method is not a very fair example, it is heavily overloaded. Suppose print
isn't overloaded, its parameter type is fixed, so T
can 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.
Try this generics kung fu:
public static int sumSomeInts(List<Pair<Integer, Double>> values) {
Collection<Integer> ints = Collections2.transform(values,
Pair.<Integer, Double>deferredFirst());
int sum = 0;
for(int i : ints)
sum += i;
return sum;
}
You can type the method call and pass the generics through to the next call.
I'm not sure about the exact generic parameters to use here, because you haven't included enough code. If you paste in the whole method where the problem is I will edit this answer to make it compile. EDITED: with new info from question
Please let me know if it compiles. If it isn't that solution, it will be close. The key is to type the static method using Class.<Type>staticMethod()
syntax.
What I came up with a while ago is:
@SuppressWarnings("rawtypes")
private static final Function ExtractFirst = new Function() {
@Override
public Object apply(final Object from) {
Preconditions.checkNotNull(from);
return ((Pair)from).first;
}
};
@SuppressWarnings("unchecked")
public static <A> Function<Pair<A,?>,A> extractFirst() {
return ExtractFirst;
}
Don't let the "SuppressWarnings" put you off, it works fine.
Example:
List<Pair<String,String>> pairs = ImmutableList.of(Pair.of("a", "b"),
Pair.of("c", "d"),Pair.of("e", "f"));
Iterable<String> firsts = Iterables.transform(pairs,
Pair.<String>extractFirst());
Unfortunately, yes, you have to supply the generic argument to extractFirst(). I think this is the best you'll get though.
精彩评论