views:

312

answers:

5
class Test1<T> {
  Test1(Class<T> type) {}
}

class Test2 extends Test1<Class> {
  Test2() {
    super(Class.class);
  }
}

This works, but, I am warned about use of a raw generic type in "Test1<Class>". I understand that, but, changing it to "Class<?>" (which should be equivalent?) gives a compiler error in the call to super() -- Class.class is apparently of type "Class<Class>" instead of "Class<Class<?>>".

Variants on these all seem to result in a compile error.

Can anyone see how to resolve this, such that I am always using a type param with Class, and hence don't get an IDE warning? It seems better that way, even if it works as-is. I am open to changes in the structure of these two classes two as long as it preserves the basic intent: Test2 is a special type of Test1, specialized for "Class" objects.

+1  A: 

I use Java 1.6.0_14 and the compiler doesn't give me any warnings when compiling that code.

cd1
Oops, will clarify. Yeah javac takes it. My IDE rightly suggests this isn't ideal -- Class should always have a type param. It's not a big problem in practice, wondering if there is a way to do this while always using the type param on Class though.
Sean Owen
Does javac still remain silent when using javac -Xlint:unchecked ? If it does, you should report a bug against your IDE vendor.
Robert Munteanu
+1  A: 

Class and Class<?> aren't exactly equivalent. They're semantically equivalent, but the latter is effectively telling the compiler, "Yes, I know this is a generic type, but I don't know anything about the type parameter." The former is just the raw type, which can often be a sign that it's old code which hasn't been migrated to use generics.

This is covered in the Java Generics FAQ - see the question "What is the difference between the unbounded wildcard parameterized type and the raw type?" in the parameterized types section.

Jon Skeet
+1  A: 

This might be one of those times that is appropriate for:

@SuppressWarnings("unchecked")
class Test2 extends Test1<Class> {
  Test2() {
    super(Class.class);
  }
}

Unless you will always use Test1<Class>, I don't see another way to resolve this warning. (You'd probably get the same warning if you ran javac -Xlint:unchecked, but I'm not positive about this.) The issue here is that you will normally (I assume) be using your class Test1 with ungeneric values, but Class is a genericized class that takes a Class object as its parameter. I don't see any way around this, so I think the best solution is to suppress this specific warning so that the real problems will stand out.

Eddie
Yeah, that is a good way to punt on it indeed, and probably the most practical response. I was curious if there was actually a way to manage it without a suppression. -Xlint:all doesn't make javac complain about this.
Sean Owen
+2  A: 

Your warnings stem from the fact that Class is itself a paramterized class: public final class Class<T>. The simplest way to remove the warnings is to make the T the type parameter instead of Class<T>:

class Test1<T> {
    Test1(T type) { Class<?> classType = type.getClass();}
}

class Test2 extends Test1<String> {
    Test2() {
        super("");
    }
}
Robert Munteanu
Yeah, some slight variant on this is probably the least messy workaround. While I will probably @SuppressWarnings, glad to confirm that this is in fact how you'd have to do it.
Sean Owen
A: 

The short answer is that there is no way to specify the type parameter. That is because the Class object (say, for example, List.class, not just Class.class) doesn't understand that generic type parameter - it is erased during compilation.

My IDE, Intellij IDEA, gives the same warning. It is the IDE being too aggressive. You can suppress the warning with IDEA (it generates a @SuppressWarnings annotation), but code to work around it (like Robert Munteau's clever workaround) is really introducing some nasty design just to avoid an IDE message that is really not correct - the JDK does not view this case as an erasure.

EDIT: Given the downvote, I should at least justify my thinking here.

Think about the following:

 List.class

That represents a class object of type:

 Class<List>

It cannot represent a type of:

 Class<List<?>>

because it isn't known if the given list will actually have a generic parameter or what it will be. The List.class object has to represent all types, as far as parameterizing Class.

The evidence that the JDK does not view this as an unsafe operation is that if you compile the code, it gives you no warning.

So the IDE is just being overly sensitive. Although the questioner didn't specify which IDE he uses, I see this behavior with Intellij IDEA 8.1.2.

Yishai