tags:

views:

168

answers:

4

Can't figure out this generics problem. I have these interfaces:

public interface LoadableObject
{
}
public interface LoadableObjectFactory<T>
{
}

And now I want to do this:

public class ObjectReference<T extends LoadableObject>
{
    Class<? extends LoadableObjectFactory<T>> _cls;

    public ObjectReference(LoadableObjectFactory<T> obj)
    {
        _cls = obj.getClass();
    }
}

But I get an error:

incompatible types
found   : java.lang.Class<capture#885 of ? extends test.LoadableObjectFactory>
required: java.lang.Class<? extends test.LoadableObjectFactory<T>>
  _cls = obj.getClass();
                     ^

I am able to compile if I remove the type params for LoadableObjectFactory at the definition of _cls, but then it's an incomplete type... Is there something I'm missing or is it simply impossible?

+4  A: 

getClass() in runtime provides Class<?>. Because of type erasure information about generic params is not available. You have to do a cast

    @SuppressWarnings("unchecked")
public ObjectReference(LoadableObjectFactory<T> obj)
{
 _cls = (Class<? extends LoadableObjectFactory<T>>) obj.getClass();
}

to get it working

Luno
I don't see how it is applicable - presumably this is a compiler error, and at compile-time all type information available. JavaDoc for `Object.getClass` even have this bit: "The actual result type is Class<? extends |X|> where |X| is the erasure of the static type of the expression on which getClass is called. For example, no cast is required in this code fragment:`Number n = 0; Class<? extends Number> c = n.getClass();`"
Pavel Minaev
Yes, you're right. It's probably compiler error. Nevertheless, casting is a way to bypass it.
Luno
compiler error? "...|X| is the **erasure** of the static type...". The original code is returning `Class<? extends LoadableObjectFactory>`, after `<T>` being erased.
Carlos Heuberger
+4  A: 

Here's the info about type erasure, straight from the horse's mouth, so to speak.

This stuff hurts my head to think about, so I won't try to explain it. Suffice it to say that there are some operations you'd expect to work, but because Java "forgets" the generic details of types between modules, they don't.

You're probably not in a position to use this information, but the same person who built generics into Java has gone and designed a whole new Java-like language: It's called Scala. Scala handles types and generics a whole lot more competently; you could say it does everything right in this respect. It also runs in the JVM and you can call between Scala and Java classes.

My personal take on Scala, though, is that it's a system for artfully managing complex types that incidentally can also be used as a programming language. :)

Carl Smotricz
+1  A: 

I won't point finger to 'type erasure'. Look at the code statically, it's obviously safe, and it should pass compiler in a better world.

Java Generics is too complicated, not worth your time to figure out every last details. If you know you know better than the compiler, just do whatever to bypass it.

irreputable
A: 

I would have expected the LoadableObjectFactory to look like this:

public interface LoadableObjectFactory<T extends LoadableObject>

Having said that - Object.getClass() returns an untyped class, so you have to cast it. To fix it, the would have to have changed the signature of java.lang.Object to

class Object<T extends Object<T>> {
    native Class<T> getClass();
}

but alterations to java.lang.Object would have involved pretty major surgery just about everywhere.