World Line

Java puzzler: Generic type inference

with 3 comments

Here’s an issue I ran into while hacking a type system to work with some very inflexible generated code; I’ve distilled the strangeness into the simplest form I could.

Suppose you have a simple type hierarchy:

public class A { }
public class B extends A { }
public class C extends A { }

Then, it turns out that the following code works just fine:

List<Class<? extends A>> list = ImmutableList.of(B.class, C.class);

Surprisingly, however, the following code fails to compile(!):

List<Class<? extends A>> list = ImmutableList.of(B.class);

with the error

incompatible types
found   : com.google.common.collect.ImmutableList<java.lang.class<B>>
required: java.util.List<java.lang.class<? extends A>>
    List<Class<? extends A>> list = ImmutableList.of(
    ...

 

That’s right; adding the parameter makes this typesafe! (Note that my example uses ImmutableLists from the Google Common Collections, but this is actually happening because of Java’s type inference mechanism, not anything related to that particular class). Of course, it’s easy to work around it, but the interesting thing is what can be learned about Java by understanding the problem here.

I’ll post a “solution” in a few days, in case anyone wants to give it a shot.

Written by Adrian Petrescu

October 13, 2010 at 12:29 am

Posted in Computer Science, Development

Tagged with

3 Responses

Subscribe to comments with RSS.

  1. One must remember to realize that if A is a subclass of B, SomeClass<A> is NOT a subclass of SomeClass<B>. eg (which would cause an error):

    ArrayList<Object> foo = new ArrayList<Integer>();

    The reason why the first case worked was because when the compiler finds 2 different classes (extendable to arbitrary number of classes), it tries to find the common superclass and interface and then it becomes an object of that type. There is a simpler example of this first case, but I am too lazy to write it up.

    I am interested in seeing the solution to this specific problem, since the class Class is special and casting B.class back up does not work.

    William

    October 14, 2010 at 4:57 am

  2. Yes, that’s pretty much correct — the nearest common ancestor when it’s inferring based on B and C is A, but either B or C by itself can only infer to themselves, which is not compatible with List<Class<? extends A>> since generic types are not contravariant.

    The practical solution is to use Builder. The most straightforward solution however, as suggested by Minsang, is to parametrize of():

    ImmutableList.<Class<? extends A>>of(B.class);

    (Also, I fixed up the formatting of your reply. Not your fault, the escaping here just sucks.)

    Adrian Petrescu

    October 14, 2010 at 5:00 am

  3. Wow, never knew you can parametrize functions like that. Sweet.

    William

    October 29, 2010 at 4:13 am


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: