Difference Between an Unbound Wildcard and a Raw Type

Difference between an unbound wildcard and a raw type

How List<?> differs from List<Object>

The main difference is that the first line compiles but the second does not:

List<?> list = new ArrayList<String> ();
List<Object> list = new ArrayList<String> ();

However, because you don't know what the generic type of List<?> is, you can't use its parameterized methods:

List<?> list = new ArrayList<String> ();
list.add("aString"); //does not compile - we don't know it is a List<String>
list.clear(); //this is fine, does not depend on the generic parameter type

As for the difference with raw types (no generics), the code below compiles and runs fine:

List list = new ArrayList<String> ();
list.add("aString");
list.add(10);

What's the difference between unbounded wildcard type List? and raw type List?

Here's a summary of the three:

  • List: A list with no type parameter. It is a list whose elements are of any type -- the elements may be of different types.

  • List<?>: A list with an unbounded type parameter. Its elements are of a specific, but unknown, type; the elements must all be the same type.

  • List<T extends E>: A list with a type parameter called T. The supplied type for T must be of a type that extends E, or it is not a valid type for the parameter.

Understanding Raw vs Unbounded Wildcard in Static Nested Class Type Definition

  1. Although called Inner in the example, it's not actually an inner class, but a static nested class. Inner classes are non-static nested classes (see https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html).
  2. The fact that it's a static nested class instead of a top-level class doesn't matter here.
  3. Code using class Inner can either use the raw type Inner (in which all type checks are bypassed - not interesting here), or specify an actual type for the type parameter U. In the latter case the upper bound restricts that type to subtypes of the generic type Outer<T> where T can be any type, regardless of whether Inner is declared as Inner<U extends Outer> or Inner<U extends Outer<?>>.

The use of a raw type in the class signature still makes it possible to use strong type checking when declaring variables or parameters. For example, the following will compile (assuming Inner has a no-args constructor):

Outer.Inner<Outer<String>> x = new Outer.Inner<Outer<String>>();

But replacing Outer<String> on either side (but not on the other) with Outer will yield a compiler error. This behavior would be exactly the same in case an unbounded wildcard would be used instead of a raw type, so no difference so far.

The actual difference is in how the the class Inner is allowed to use variables of type U. Suppose you are passing in such a variable in the constructor:

public Inner(U u) { this.u = u; }

Suppose also that Outer has a method that takes an argument of type T (its own type parameter), e.g.:

void add(T) { ...}

Now, in case of the raw upper bound (U extends Outer), it would be legal for code in class Inner to call this method with any object, e.g. a String:

this.u.add("anything")

although a compiler warning would be issued (unless suppressed), and in case the actual runtime type T would be different from String, a ClassCastException would be thrown in code that depends on the object being a different type.

In the case of an unbounded wildcard however (U extends Outer<?>), since T is a specific but unknown type, calling the add method will result in a compiler error regardless of which argument you give it.

Since you mention the code compiles fine in both cases, such method consuming T either doesn't exist in Outer, or it is not being called from Inner. But by adding the unbounded wildcard, you can prove to the users of the class that this is not happening (because otherwise the code wouldn't compile).

In order to allow calling this.u.add(s) with s being a String argument without using a raw type for the upper bound, Inner would have to be declared as Inner<U extends Outer<? super String>>, following the PECS principle, since U is the type of a consumer in this case.

situations where an error will be caught by using unbounded wildcards, but will only be flagged as a warning if you use raw type

Well, you could adapt some of the examples of that same chapter to reproduce a case of this.

For example, consider the following generic class:

class Node<E> {
private E value;
@Override public String toString(){ return value.toString(); }
}

Now, imagine that you write the following piece of code, which is wrong:

Object o = new Node<Integer>(10);
Node<String> node = new Node<>("Hello");
if(o instanceof Node) {
Node other = (Node) o;
other.value = node.value; //Uh oh! Warning
}
System.out.println(node); //Hello
System.out.println(other); //Hello - WTH!

If you try to compile this, you would only get a warning, but it would still compile just fine:

javac -Xlint:unchecked Node.java

Node.java:21: warning: [unchecked] unchecked assignment to variable value as member of raw type Node
other.value = node.value;
^
1 warning

However, if you change the code to use unbounded wildcards:

Object o = new Node<Integer>(10);
Node<String> node = new Node<>("Hello");
if(o instanceof Node<?>) {
Node<?> other = (Node<?>) o;
other.value = node.value; //Uh oh! Compiler error
}

Now you would get the following error when compiling:

javac -Xlint:unchecked Node.java

Node.java:21: error: incompatible types: String cannot be converted to CAP#1
other.value = node.value;
^
where CAP#1 is a fresh type-variable:
CAP#1 extends Object from capture of ?
1 error

So, as you can see the unbounded wildcard offers better type checking guarantees than the raw type.

Difference between raw types and ? in Generics

ArrayList<?> simply means "any type." In other words, any type of ArrayList can be assigned to such variable. That could be ArrayList<Integers>, ArrayList<JButton> or anything else. Using the wildcard alone, without the keyword super (followed by a type), means that you cannot ADD anything to the list defined as ArrayList<?>. ArrayList alone however, means the old style type-less ArrayList which you can do all sorts of operations including add.

List<?> list;
List<Integer> ints = new ArrayList<Integer>();
List<Integer> strings = new ArrayList<Integer>();
list = ints; // valid
list = strings; // valid
list.add("new"); // compile error

UPDATE:

Suppose I have following method:

void insert(List list) {
// loop through list, do whatever you like
list.add("my string"); // dangerous operation
}

Now if I call insert(ints) compiler will generate a warning but will not prevent me of adding a String to a list of integers. Changing method to following:

void insert(List<?> list) {
// loop through list, do whatever you like
list.add("my string"); // compiler error on this dangerous operation
}

would prevent me of doing such an operation.

Java generics, Unbound wildcards ? vs Object

There are two separate issues here. A List<Object> can in fact take any object as you say. A List<Number> can take at least Number objects, or of course any subclasses, like Integer.

However a method like this:

public void print(List<Number> list);

will actually only take a List which is exactly List<Number>. It will not take any list which is declared List<Integer>.

So the difference is List<?> will take any List with whatever declaration, but List<Object> will only take something that was declared as List<Object>, nothing else.

The last quote simply states, that List<?> is a list for which you literally don't know what type its items are. Because of that, you can not add anything to it other than null.

Type parameter vs unbounded wildcard

The Java Generics FAQ is a great source to answer these kind of questions and the "wildcard vs. generic" one is discussed at length in Which one is better: a generic method with type parameters or a non-generic method with wildcards? and the subsequent Case Studies.

Angelika Langer comes to the conclusion:

Conclusion: In all these examples it is mostly a matter of taste and style whether you prefer the generic or the wildcard version. There is usually trade-off between ease of implementation (the generic version is often easier to implement) and complexity of signature (the wildcard version has fewer type parameters or none at all).

Simpler method signature -> easier to understand (even if both are used the same way) -> good in public API (tradeoff: more complex implementation)

But the whole thing is a lightweight issue and in my experience is consistency over the whole API much more important than which style you use.

What is the use and point of unbound wildcards generics in Java?

An unbound type can be useful when your method doesn't really care about the actual type.

A primitive example would be this:

public void printStuff(Iterable<?> stuff) {
for (Object item : stuff) {
System.out.println(item);
}
}

Since PrintStream.println() can handle all reference types (by calling toString()), we don't care what the actual content of that Iterable is.

And the caller can pass in a List<Number> or a Set<String> or a Collection<? extends MySpecificObject<SomeType>>.

Also note that not using generics (which is called using a raw type) at all has a quite different effect: it makes the compiler handle the entire object as if generics don't exist at all. In other words: not just the type parameter of the class is ignored, but also all generic type parameters on methods.

Another important distinctions is that you can't add any (non-null) value to a Collection<?>, but can add all objects to the raw type Collection:

This won't compile, because the type parameter of c is an unknown type (= the wildcard ?), so we can't provide a value that is guaranteed to be assignable to that (except for null, which is assignable to all reference types).

Collection<?> c = new ArrayList<String>();
c.add("foo"); // compilation error

If you leave the type parameter out (i.e. use a raw type), then you can add anything to the collection:

Collection c = new ArrayList<String>();
c.add("foo");
c.add(new Integer(300));
c.add(new Object());

Note that the compiler will warn you not to use a raw type, specifically for this reason: it removes any type checks related to generics.

when to use Unbounded Wild Card in Java

Are you asking why do we need the notation

List<?> 

when the simpler

List 

is equivalent?

Using the wildcard is more explicit but the simpler syntax is required for backwards compatibility with pre-generics code. So you can use either interchangeably but there is also a third way of declaring this list which is also equivalent.

List<? extends Object> list;

This list will contain only objects of type Object. Since everything in Java extends from Object this is basically the same as saying unbounded.

Personally of the three I would probably use the first one because it is concise while still explicitly stating what the List should contain. If you just declared it as a List without a generic argument then it could be mistaken for a pre-generics list.

When would you ever want to use this? From experience I have only used this kind of unbounded list with legacy code. I was always taught to only put objects of one common type into a list (like a list of customers etc). However, people don't always write nice code and at times I've had to deal with lists containing different types. In these cases it was hard to move to a proper genericised list so I had to make do with an unbounded list.

It is possible that you may have a legitimate cause for an unbounded list. It depends what you want to do with those objects. If once they've been added to the list you only need to call methods from the Object class (like toString) or perhaps the List interface (like size), then perhaps you have a genuine use case for an unbounded list.

Is it backwards-compatible to replace raw type like Collection with wildcard like Collection??

It is not safe at runtime to make this replacement.

I should perhaps say more precisely that this change is safe by itself; but that subsequent changes that it encourages could lead to failures.

The difference between a Collection and a Collection<?> is that you can add anything to the former, whereas you cannot add anything except literal null to the latter.

So, somebody currently overriding your method might do something like:

@Override
public StringBuilder append(Collection value) {
value.add(Integer.valueOf(1));
return new StringBuilder();
}

(I don't know what the method is meant to be for; this is a pathological example. It certainly looks like something they shouldn't do, but that's not the same as them not doing so).

Now, let's say this method is called like so:

ArrayList c = new ArrayList();
thing.append(c);
c.get(0).toString();

(Again, I don't know how it is used for real. Bear with me)

If you changed the method signature to append Collection<?> in the superclass, perhaps surprisingly (*), you would not need to update the subclass to be generic too: the append method above would continue to compile.

Seeing the new generic type of the parameter in the base class, you could then think that you could now make this calling code non-raw:

ArrayList<Double> c = new ArrayList<>();
thing.append(c);
c.get(0).toString();

Now, the gotcha here is how the last line is evaluated: there is an implicit cast in there. It would actually be evaluated something like:

Double d = (Double) c.get(0);
d.toString();

This is despite the fact you can invoke toString() on an Object: there is still a checkcast inserted by the compiler, to the erasure of the list element type. This would fail at runtime, because the last item in the list is an Integer, not a Double.

And the key point is that no cast is inserted for the raw-typed version. That would be evaluated like:

Object d = (Object) c.get(0);
d.toString();

This would not fail at runtime, because anything can be cast to object (in fact, there would be no cast at all; I am merely inserting it for symmetry).

This is not to say that such calling code could not exist before making the parameter Collection<?>: it certainly could, and it would already fail at runtime. But the point I am trying to highlight is that making this method parameter generic could give the mistaken impression that it is safe to convert existing raw calling code to use generics, and doing so would cause it to fail.

So... Unless you can guarantee that there is no such insertion in subclasses, or you have explicitly documented that the collection should not be modified in third method, this change would not be safe.


(*) This arises as a consequence of the definition of override-equivalence, in JLS Sec 8.4.2, where erasure is explicitly considered.



Related Topics



Leave a reply



Submit