So I have gone through official java tutorial,https://docs.oracle.com/javase/tutorial/java/generics/index.html , also searched through stackoverflow, and it turns out that there is not much difference between using <E>
and <?>
, one as I can understand is normal generic form and the other one is wildcard. So far the only difference I came across is that when using <E>
or <E extetnds BlaBlaClass>
we can refer to the type as E
, while otherwise we don't know any information about the collection or array or the type at all.
My question is: is there any advantage of using <?>
(wildcard) over normal generics <E>
? and if so, what is the scenario of this situation? And why would someone use wildcards at all?
I have looked at Difference between generic type and wildcard type , Difference between ? (wildcard) and Type Parameter in Java , When to use generic methods and when to use wild-card? , Java Generics ? , E and T what is the difference? . So far it looks like <?>
is poorer version of <E>
The unbounded wildcard ?
is useful in cases where it doesn't matter what the generic type is. Let's say you have a method that checks a list's size and clears it if it's too large, and you want it to accept lists with any type of elements:
public static <E> void checkList(List<E> list, int max) {
if (list.size() > max) list.clear();
}
You declare the type variable E
, but the size()
and clear()
methods don't need it, so it goes unused. Instead, you can do this:
public static void checkList(List<?> list, int max) {
if (list.size() > max) list.clear();
}
This simplifies the method declaration, and makes it clear to other programmers that this method isn't concerned with the type of the list elements.
Unbounded wildcards can also be used in field or local variable declarations (where type variables cannot be declared) to allow assignment of values with any generic type.
public static void main(String[] args) {
List<?> list;
list = new ArrayList<Object>();
list = new ArrayList<String>();
list = new ArrayList<Integer>();
}
If this was List<Object>
, the last two lines would not compile. It could be <? extends Object>
, but this is equivalent to <?>
.
For a slightly more practical example, say you wanted a map where the values can be any list:
public class MyClass {
public final Map<String, List<?>> lists = new HashMap<>();
}
Finally, if you ever need to cast a value to a generic class and you aren't certain of its type parameters, you must use ?
for them. (Never use raw types, it disables generic type safety checks.) A good example of this is in the common equals()
implementation:
@Override
public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
MyClass<?> other = (MyClass<?>) obj;
if (!myField.equals(other.myField)) return false;
return true;
}
For more info on Java generics, check out Angelika Langer's Generics FAQ.
Let's say you're working with instances of some class Foo.
List<Foo>
or List<? super Foo>
but not to a List<?>
or List<? extends Foo>
.List<Foo>
or List<? extends Foo>
but not from a List<?>
or List<? super Foo>
(it'll come back typed as just Object)So, that's the advantage: you get to interact with the list in terms of its Foo elements.
If all you care about is whether the List is empty, or something else that doesn't rely on the specific shape of its elements, then a List<?>
is fine for that.
This applies just as well to a situation where you're in a generic method, and are working on instances of a generic E
rather than a specific class Foo:
public <E> void addIfEmpty(E element) {
List<? super E> listOfE = ...; // or List<E>
if (!listOfE.isEmpty()) {
listOfE.add(element);
}
List<?> listOfWild = ...;
if (!listOfWild.isEmpty()) {
listOfWild.add(element); // compilation error
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With