Difference Between Unbounded Wildcard, Raw Type and Object
Natural questions to ask are, What good is the unbounded wildcard anyway? Why not just use the raw type? How do unbounded wildcard instantiation and raw types compare? The first difference is that the compiler will issue unchecked warnings when we use methods of the raw type. For example:
Why is the compiler warning us? It’s because it cannot stop us from abusing our raw type by foisting the wrong type of objects on it. But, when using an unbounded wildcard, the compiler guarantees that we are safe by allowing us only the operations that it knows are safe-namely, reading as the type of the upperbound of the type parameter of that class or Object (the upper bound of everything). The compiler does not let us write to an unbounded wildcard at all. For example:
So why use the unbounded wildcard? To play by the rules of generics and guarantee that we don’t do anything unsafe. Next, is an unbounded wildcard instantiation is similar to an instantiation on the type Object? Remember that a Gen<?> holds some instantiation of Gen. It could be a Gen<Integer> for all we know. But a Gen<Object> is actually a Gen that holds concrete Object types. The Gen<Object> can be read and written as Object. For example:
The Gen<?> can only be read (not written) and only read as type of the upperbound of the type parameter of that class or Object. The elements of Gen<?> are actually all of some unknown type. The elements of the unknown type Gen all have a common supertype that could be Object or some other common type that is more restrictive than Object. The knowledge of what “could be” in the Gen<?> doesn’t do much for us in practice, but means something completely different from Gen<Object>.
Finally, how Gen<Object> and the raw type compare? In fact, the raw type after erasure is effectively Gen<Object>. But in this case, we’re telling the compiler that this is OK. Here, we are asking for a type with elements that can hold any type safely and the compiler obliges. The answer to the question of how Gen<Object> and the raw type Gen compare is that Gen<Object> is the “generic safe” version of the raw type.