tags:

views:

438

answers:

5

The following code does not compile:

public class GenericsTest {    
    public static void main(String[] args) {
        MyList<?> list = new MyList<Object>();
        Class<?> clazz = list.get(0);

        // Does not compile with reason 
        // "Type mismatch: cannot convert from Object to Class"
        MyList list2 = new MyList();
        Class clazz2 = list2.get(0);
    }
    static class MyList<T> extends ArrayList<Class<T>> {
    }
}

I wanted to do this to introduce generics to old code without breaking the build.

Is this a bug in both the compiler (both eclipse and javac) or am I missing something here? What other possibility exists to introduce generics to MyList?

EDIT

For clarification:

I have the generic class

public class MyList extends ArrayList<MyObject> {}

with

public class MyObject {}

and code using MyList

MyList list = new MyList();
...
MyObject o = list.get(0);

Now during development I see I want to introduce generics to MyObject

public class MyObject<T> {}

and now I want to have this new generic thingy in MyList as well

public class MyList<T> extends ArrayList<MyObject<T>> {}

But that does break my build. Interestingly

public class MyList<T> extends ArrayList<MyObject<T>> {
    public MyObject<T> get(int i) {
        return super.get(i);
    }
}

will allow old code

MyList list = new MyList();
...
MyObject o = list.get(0);

to compile.

OK, seems that when I introduce this generic, I will have to live with having to change all calls to MyList to the generic form. I wanted the old code to just introduce a warning instead of an error.

A: 

Have you tried:

Class clazz2 = list2.get(0).getClass();

Read about it at: http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Object.html#getClass()

Bogdan
But that is not the same! String.class != String.class.getClass()
Tobias Schulte
+11  A: 

I think you are not understanding quite how generics work.

MyList<?> list = new MyList<Object>();
Class<String> clazz= list.get(0);

This code snippet does not compile because you are telling the compiler that list is going to hold Class<Object> types - and then in the next line you are expecting it to return you a Class<String>. The generic system in Java is not capable of converting types used with generics based on inheritance like you might think it would.

If you expect list to hold Class<String>, then you need to declare it as so - or, if you want it to be able to hold any types, then you cannot do the second line without a cast.

MyList<String> list = new MyList<String>();
Class<String> clazz = list.get(0);

or

MyList<?> list = new MyList<Object>();
//generates a warning about an unchecked cast
Class<String> clazz = (Class<String>) list.get(0);


The second code snippet does not work because when you use raw types, you still need to cast the Object returned by get() to the declared type you are using (which has always been the case).

MyList list2 = new MyList();
Class clazz2 = (Class) list2.get(0);
matt b
I think you have not seen the declaration of the class MyList at the end: static class MyList<T> extends ArrayList<Class<T>>. There is defined, that MyList<T> takes Class<T>-Objects.
Mnementh
Right, I caught that at the end - but he is expecting MyList<Object> to be convertible to MyList<String>, which it is not.
matt b
No, I am not expecting MyList<Object> to be convertible to MyList<String>. I want MyList to be convertible to MyList<?>. Sorry, my example had an error.
Tobias Schulte
Well with your corrected example, my second answer still applies - the get() method on the raw List returns an Object, so you need to explicitly cast it to a Class.
matt b
+1  A: 

I don't have a compiler on this machine, but this should work.

public class GenericsTest {    
    public static void main(String[] args) {
        MyList<Object> list = new MyList<Object>();
        Class<?> clazz= list.get(0);

    }


    static class MyList<T> extends ArrayList<Class<? extends T>> {
    }
}
Itay
Yes, but that does not work.
Tobias Schulte
Yes it does. There is a runtime bug since it call get() on a empty list but the code compile.
Julien Grenier
Yes, your code compiles, but neither "Class clazz = list.get(0);" nor "MyList<String> list = new MyList<String>(); Class<String> clazz = list.get(0);". "Class<? extends String> clazz = list.get(0);" would work, but that is not the intended use.
Tobias Schulte
+1  A: 

In your example is MyList the old code you want to update to support generics? If so, what type of objects is my list supposed to contain?

As was mentioned here elsewhere that compilation error is valid. It is due to the fact that a raw list is being accessed without the get being cast to Class. Hence the code is attempting to assign an Object to a reference of a Class. It is equivalent to this:

Object o = new Object();
Class c = o;

Which simply cannot compile. Also, to fully utilize generics you should favor Class<?> instead of Class.

laz
A: 

In your example the second example doesn't work because you are not using generics so it means that the get() method will return a Object and you will need to cast it to a Class. When you are not specifying a type for your MyList :

MyList list2 = new MyList();

Then you are losing the Generics advantages and you need to cast the object when calling the get() method just like in the good ol' days.

Julien Grenier