Reputation: 13541
I'm quite new in Java, although I have much experience in C++ and other languages. So templates/generics are not something I don't know.
There's something that bothers me though, it is this <?>
that I was told I should use everytime I use a generic instance of something when I don't know in advance of which specific type it will be:
Like:
List< MyGeneric > foo; // bad
List< MyGeneric<?> > bar; // good
IntelliJ doesn't barf on me when using the first expression, and I don't understand why it should. My coworkers have expressed that the 2nd expression was much better, but couldn't tell me exactly why.
I mean, what exactly is the difference between these two, apart from the second being explicit about the fact that it is a generic that we manipulate ?
The compiler certainly knows that it is a generic at compile time, so my guess is that the second expression is only better because it tells the programmer that he is manipulating a generic.
Am I right?
Edit: for clarification, I ovbiously use the most restrictive type, like List<MyGeneric<Double>>
, whenever I know in advance what I am going to store in there. My question is for when I store unknown types of generics.
Upvotes: 0
Views: 94
Reputation: 489
List<?>
means a list typed to an unknown type. This could be a List<A>
, a List<B>
, a List<String>
etc.
Since the you do not know what type the List
is typed to, you can only read from the collection, and you can only treat the objects read as being Object
instances. Here is an example:
public void processElements(List<?> elements) {
for(Object o : elements){
System.out.println(o);
}
}
The processElements()
method can now be called with any generic List
as parameter. For instance a List<A>
, a List<B>
, List<C>
, a List<String>
etc. Here is a valid example:
List<A> listA = new ArrayList<A>();
processElements(listA);
Following tutorials will further help you to understand it:
Upvotes: 0
Reputation: 140309
Let's give an example of why it's bad to use the first. Assuming MyGeneric
is defined like this:
class MyGeneric<T> {
private final T instance;
MyGeneric(T instance) { this.instance = instance; }
T get() { return instance; }
}
The following code would compile and run, but fail at runtime with a ClassCastException
:
List<MyGeneric> list = new ArrayList<>();
list.add(new MyGeneric<>("Hello"));
for (MyGeneric instance : list) {
Integer value = (Integer) instance.get(); // Compiles, but fails at runtime.
}
This compiles because you're using raw types: the compiler doesn't know that instance.get()
can't return an Integer
; it would merely warn you that it might be unsafe.
On the other hand, the following code would not even compile:
List<MyGeneric<String>> list = new ArrayList<>();
list.add(new MyGeneric<>("Hello"));
for (MyGeneric<String> instance : list) {
Integer value = (Integer) instance.get(); // Won't compile, incompatible types.
}
Upvotes: 4
Reputation: 159086
The difference is that a raw type ignores the fact that the class is a generic, while the wildcard <?>
specifies that the class is a generic but the type argument is unknown.
Raw means that you lose all compiler type-checking. Wildcard keeps type-checking intact.
Example:
public class MyGeneric<T> {
private T val;
public T get() {
return this.val;
}
public void set(T val) {
this.val = val;
}
}
MyGeneric a = new MyGeneric<Integer>();
a.set("Foo"); // accepted
Setting the value for a
to a String
when it was declared to be an Integer
is accepted by the compiler, because a
was defined raw, which means that the compiler is ignoring the fact that the class is a generic. When val
is later used as an Integer
, the program will crash. It's a bomb waiting to go off.
MyGeneric<?> b = new MyGeneric<Integer>();
b.set("Bar"); // compile error
Trying to set the value for b
will not compile:
The method set(capture#1-of ?) in the type MyGeneric<capture#1-of ?> is not applicable for the arguments (String)
Here the compiler knows that the class is a generic and will not allow setting the value to anything (even an Integer
), because it doesn't know what type would be allowed (wildcard = unknown, remember?). The compiler safeguards here, as it should.
Upvotes: 3
Reputation: 106390
Every time? It's not applicable always, and it doesn't always make sense.
Let's describe what that actually is: <?>
is an unbound wildcard, which immediately implies two things:
MyGeneric
is a generic class, butIt is preferable to the first expression in that the first expression always guarantees that you'll be working with a raw type, and you really don't want to use raw types. However, it is a gross overgeneralization to assume that using an unbound wildcard every time would be ideal.
If you actually know or care the type, or know or care about its bounds, use that instead.
Upvotes: 9