可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
If I declare a Class as a field:
Class fooClass;
Eclipse gives me the warning:
Class is a raw type. References to
generic type Class should be
parametrized
What does this mean in practice? and why am I urged to do it? If I ask Eclipse for a "quick fix" it gives me:
Class<?> fooClass;
which doesn't seem to add much value but no longer gives a warning.
EDIT: Why is Class generic? Could you please give an example of parameterization, i.e. could there be a valid use of something other than <?>
?
EDIT: WOW! I had not realized the depths of this. I have also watched the Java Puzzler and it's certainly scared me about the bear traps. So I will always use
Class<MyString> myStringClass = MyString.class;
rather than
Class myStringClass = MyString.class;
(But having used Java from day one, I didn't really notice when Class became generic);
NOTE: I have accepted @oxbow_lakes as this makes sense to me, but it is clearly a very complicated area. I would urge all programmers to use the specific Class<MyString>
rather than Class
. And Class<?>
is much safer than Class
.
回答1:
Raw Types and Unbounded Wildcards
None of the previous answers have really addressed why you should prefer Class<?>
over Class
, as on the face of it, the former seems to offer no more information than the latter.
The reason is that, the raw type, i.e. Class
, prevents the compiler from making generic type checks. That is, if you use raw types, you subvert the type-system. For example:
public void foo(Class<String> c) { System.out.println(c); }
Can be called thusly (it will both compile and run):
Class r = Integer.class
foo(r); //THIS IS OK (BUT SHOULDN'T BE)
But not by:
Class<?> w = Integer.class
foo(w); //WILL NOT COMPILE (RIGHTLY SO!)
By always using the non-raw form, even when you must use ?
because you cannot know what the type parameter is (or is bounded by), you allow the compiler to reason about the correctness of your program more fully than if you used raw types.
Why have Raw Types at all?
The Java Language Specification says:
The use of raw types is allowed only as a concession to compatibility of legacy code
You should always avoid them. The unbounded wildcard ?
is probably best described elsewhere but essentially means "this is parameterized on some type, but I do not know (or care) what it is". This is not the same as raw types, which are an abomination and do not exist in other languages with generics, like Scala.
Why is Class Parameterized?
Well, here is a use-case. Suppose I have some service interface:
public interface FooService
And I want to inject an implementation of it, using a system property to define the class to be used.
Class<?> c = Class.forName(System.getProperty("foo.service"));
I do not know at this point that my class, is of the correct type:
//next line throws ClassCastException if c is not of a compatible type
Class<? extends FooService> f = c.asSubclass(FooService.class);
Now I can instantiate a FooService
:
FooService s = f.newInstance(); //no cast
回答2:
which doesn't seem to add much value
but no longer gives a warning.
You're right. But this might add value:
Class<FooClass> fooClass;
or, if more appropriate:
Class<? extends FooClass> fooClass;
or
Class<FooInterface> fooClass;
As with generics in general, you can improve type safety by specifying what kind of class you want the variable to hold. The warning against raw types is just meant to catch pieces of code where this potential is not used. By declaring Class<?>
you're basically saying "this is meant to hold any kind of class".
回答3:
Because, since JDK 5, Class
has now have parameterized type, which makes Class
a generic object. This is necessary (since the introduction of Generics) for the compiler to do type checking (at compile time, of course).
Class<?>
means a "class of unknown" where ?
is a generics wildcard. It means a that fooClass
of type Class<?>
accepts a Class
whose type matches anything.
Typical example:
Class<?> classUnknown = null;
classUnknown = ArrayList.class; //This compiles.
You can, effectively, provided a Parameterized Type to be more specific, e.g.:
Class<ArrayList> = ArrayList.class;
PS Bear in mind, that Class<List> listClass = ArrayList.class;
won't compile (even though ArrayList is of List) but (as Mark Peters mentioned on the comment) Class<? extends List> listClass = ArrayList.class;
does compile (thanks to the wildcard).
回答4:
The Javadoc of the Class class does give some idea about why type parameters exist for this class:
T - the type of the class modeled by
this Class
object. For example, the
type of String.class
is Class<String>.
Use Class<?>
if the class being
modeled is unknown.
The use of this type parameter is not so obvious, but a cursory look at the source code of the class, indicates why the type parameter is sometimes necessary. Consider the implementation of the newInstance
method:
public T newInstance()
throws InstantiationException, IllegalAccessException
{
if (System.getSecurityManager() != null) {
checkMemberAccess(Member.PUBLIC, ClassLoader.getCallerClassLoader());
}
return newInstance0();
}
If one hasn't noticed, the type of the object returned by this method is that of the type parameter. This is useful in code that utilizes a lot of reflection, and where one would like to be extra careful to ensure that objects of the right type are being instantiated.
Considering the example in the question, if the class instance was instead declared as:
Class<String> fooClass;
Class<Integer> barClass;
String aString;
then, it is next to impossible to have the following code to compile:
aString = barClass.newInstance();
In short, if you're going to be working with class hierarchies and you wish to impose strict compile time checks to ensure that your code does not need to perform a lot of instanceof
checks, then you're better off specifying the type of the class that you wish to utilize. Specifying ?
allows all types, but there would be cases when you'll need to be more specific.
回答5:
Because using raw types instead of parameterized types has many pitfalls.
One of which is that if a raw type is used, all generics on the class are lost. Even those defined per-method.