mindas
mindas

Reputation: 26733

Generics: compilation error with the wildcard type

I've switched to Java 7 build 21 and started getting strange compilation error. For example, the code snippet below does not compile (although IntelliJ doesn't show any errors):

    1 Iterable<?> parts = ImmutableList.<String>of("one", "two");
    2 Function<?, String> function = new Function<Object, String>() {
    3    @Override
    4    public String apply(final Object input) {
    5        return input.toString();
    6    }
    7 };
    8 Iterable<String> result = Iterables.transform(parts, function);
    9 System.out.println(result);

but if I replace ? in line 2 to Object

    2 Function<Object, String> function = new Function<Object, String>() {

then the compilation succeeds.

The error I'm getting is somewhat cryptic:

error: method transform in class Iterables cannot be applied to given types;
required: Iterable<F>,Function<? super F,? extends T> 
found: Iterable<CAP#1>,Function<CAP#2,String> 
reason: no instance(s) of type variable(s) F,T exist so that argument type Function<CAP#2,String>
conforms to formal parameter type Function<? super F,? extends T> 
where F,T are type-variables:
F extends Object declared in method <F,T>transform(Iterable<F>,Function<? super F,? extends T>) 
T extends Object declared in method <F,T>transform(Iterable<F>,Function<? super F,? extends T>) 
where CAP#1,CAP#2 are fresh type-variables: 
CAP#1 extends Object from capture of ?
CAP#2 extends Object from capture of ? extends Object

Changing line 2 to be

    2 Function<? extends Object, String> function = new Function<Object, String>() {

has no effect.

I'm using JDK 1.7.0_11-b21; this used to compile ok with build 4.

Is this a javac bug or mine?

Upvotes: 2

Views: 2491

Answers (3)

mindas
mindas

Reputation: 26733

I was actually able to solve this by changing

2 Function<?, String> function = new Function<Object, String>() {

to

2 Function<? super Object, String> function = new Function<Object, String>() {

Which makes sense when you think about it - function is the consumer here (from the infamous PECS idiom).

Upvotes: 1

Alderath
Alderath

Reputation: 3874

The method signature is:

<F,T> Iterable<T> transform(Iterable<F> fromIterable, Function<? super F,? extends T> function) 

What this means for the type parameter F is:

Iterable<F> :
    F can be any type here, but will influence the type parameter to Function
Function<? super F, ? extends T> :
    the first type parameter (? super F) MUST be a supertype of F

When you type:

Iterable<?>
Function<?, String>

You are saying Iterable of anything, so it could be e.g. Iterable<Integer>. You are also saying Function from anything to String, so it could be e.g. Function<String, String>. Since String is not a superclass of Integer, you are not fulfilling the (? super F) condition.

Upvotes: 6

kutschkem
kutschkem

Reputation: 8163

This is because Function<?,String> could be any of Function<Object,String>, Function<String,String>, Function<Foo,String>. Therefore it's correct that you aren't able to input an Object into the Function, since it could be actually needing a Foo instead.

Of course, since you have Iterable<?>, you only know the Iterable spits out Objects, so you can only use a Function<Object,WhatEver> to transform it.

Upvotes: 2

Related Questions