Monday, April 26, 2010

Raw types make my head hurt

This compiles:

public static void main(final String... args) {
    final Fred fred = new Fred();
    final Integer i = fred.get(Integer.class);
}

public interface Bob {
    <T> T get(final Class<T> type);
}

public static class Fred implements Bob {
    @Override
    public <T> T get(final Class<T> type) {
        return type.cast(null);
    }
}

This does not:

public static void main(final String... args) {
    final Fred fred = new Fred();
    final Integer i = fred.get(Integer.class);
}

public interface Bob {
    <T> T get(final Class<T> type);
}

public static class Fred<Q> implements Bob {
    @Override
    public <T> T get(final Class<T> type) {
        return type.cast(null);
    }
}

But this does:

public static void main(final String... args) {
    final Fred<?> fred = new Fred();
    final Integer i = fred.get(Integer.class);
}

public interface Bob {
    <T> T get(final Class<T> type);
}

public static class Fred<Q> implements Bob {
    @Override
    public <T> T get(final Class<T> type) {
        return type.cast(null);
    }
}

And this does:

public static void main(final String... args) {
    final Fred fred = new Fred();
    final Integer i = ((Bob) fred).get(Integer.class);
}

public interface Bob {
    <T> T get(final Class<T> type);
}

public static class Fred<Q> implements Bob {
    @Override
    public <T> T get(final Class<T> type) {
        return type.cast(null);
    }
}

What is going on here?

UPDATE: Thanks to Bob Lee in the comments, I see what is going on. Using a class-level raw type results in the methods of that class also being treated as raw types, even though the type parameters of the class and method are separate. (NB — this does not apply to static methods, only instance methods.)

4 comments:

Unknown said...

Wow, that just looks horrendous. Why would the type parameter on Fred interfere at all with the type parameter on the generic method get()?

Unknown said...

Also, minor nit -- 'final' on an interface method's parameters? C'mon. 8^)

Bob said...

Once you use a raw type (as you did in "final Fred fred = new Fred();" for Fred<Q>), everything is treated as raw. get() returns Object in that case, hence your error. Your other three examples do not use raw types, at least not in the expression in question. You cast to Bob in the 4th example making things properly generic again.

Brian Oxley said...

@Bob Zowie, thanks! I was caught by surprise that the raw type on the class also "rawed" the method: I misthought they'd behave independently.

Another reason to avoid raw types altogether.