Preface: You've asked a number of questions about type erasure, including in the chat room. While this answer will address this specific question it may answer some of your other questions as well (maybe even some you haven't asked yet).
Warning: This answer is long and the question specifically asked in this post is only directly addressed at the end.
What is type erasure?
This has been covered quite well in other Q&As so I will simply link to them:
What are the rules of type erasure?
The rules of type erasure are specified in §4.6 Type Erasure of the Java Language Specification (JLS):
Type erasure is a mapping from types (possibly including parameterized types and type variables) to types (that are never parameterized types or type variables). We write |T|
for the erasure of type T
. The erasure mapping is defined as follows:
The erasure of a parameterized type (§4.5) G<T1,...,Tn>
is |G|
.
The erasure of a nested type T.C
is |T|.C
.
The erasure of an array type T[]
is |T|[]
.
The erasure of a type variable (§4.4) is the erasure of its leftmost bound.
The erasure of every other type is the type itself.
Type erasure also maps the signature (§8.4.2) of a constructor or method to a signature that has no parameterized types or type variables. The erasure of a constructor or method signature s
is a signature consisting of the same name as s
and the erasures of all the formal parameter types given in s
.
The return type of a method (§8.4.5) and the type parameters of a generic method or constructor (§8.4.4, §8.8.4) also undergo erasure if the method or constructor's signature is erased.
The erasure of the signature of a generic method has no type parameters.
For this answer we should focus on the first and fourth bullet points which say:
The erasure of a parameterized type (§4.5) G<T1,...,Tn>
is |G|
.
And:
The erasure of a type variable (§4.4) is the erasure of its leftmost bound.
Respectively. In particular, notice how the fourth bullet point only explains the erasure of type variables. Why does this matter? I'll come back to that.
Terminology
Understanding the terms is important to understanding how the rules are applied:
Generic class
Generic interface
Generic method
- Specified in §8.4.4 Generic Methods of the JLS.
- A generic method is a method which declares one or more type variables.
Generic constructor
Type variable
- Specified in §4.4 Type Variables of the JLS.
- A type variable is introduced by a type parameter declared on the generic member.
- The syntax of a type variable is:
{Annotation} TypeIdentifier
Type parameter
- Specified in many sections of the JLS. Every section I linked to for the above terms mentions type parameters.
- A type parameter is the declaration of a type variable, plus its bounds, on a generic member.
- The syntax of a type parameter is:
{TypeParameterModifier} TypeIdentifier [TypeBound]
where TypeParameterModifier
expands into Annotation
.
Parameterized type
- Specified in §4.5 Parameterized Types of the JLS.
- A parameterized type is the actual use of a generic class or generic interface with type arguments.
Type argument
Note that the difference between type parameter and type argument is similar to how Java differentiates between a method parameter and argument. When you declare a method as void bar(Object obj)
the Object obj
is a parameter. However, when you call the method like bar(someObjInstance)
the value of someObjInstance
is the argument.
Code Examples of the Terminology
Seeing some examples in code can help understand what parts of the code each term applies to.
A generic class with type parameters
public class Foo<T extends CharSequence, U> {
// class body...
}
There are two type parameters:
T extends Charsequence
- The type variable is
T
.
- The type bounds are
extends CharSequence
U
- The type variable is
U
- The type bounds are
extends Object
(implicitly defined)
The code looks similar for generic interfaces.
A generic method with a type parameter
public void <V extends Number> bar(V obj) {
// method body...
}
This method has one type parameter:
V extends Number
- The type variable is
V
.
- The type bounds are
extends Number
.
The code looks similar for generic constructors.
A parameterized type (no wildcard)
public <E extends Number> void bar(List<E> list) {
// method body...
}
There is one parameterized type:
List<E>
A parameterized type (with wildcard)
public void bar(List<? extends Number> list) {
// method body...
}
There is one parameterized type:
List<? extends Number>
- The type argument is
? extends Number
Back to the Rules
As I mentioned, it's important to note that the rules only mention the erasure of type variables. The reason this is important is because wildcards are not allowed in the places where type variables can be defined (i.e. in type parameters). A wildcard can only be used in a type argument which is a part of a parameterized type.
The erasure of a parameterized type is simply the raw type.
When Does Type Erasure Matter?
In everyday development of generic code type erasure is virtually irrelevant. One of the only times you have to know how type erasure works in some detail is when working with raw types. In an ideal and just world you'll only work with raw types when working with legacy code (from the days before Java 5) that you cannot change. In other words, unless you're forced to work with raw types you should always appropriately use generics.
However, maybe you're forced to work with raw types or you're simply curious. In that case, you need to know how type erasure works because the erasure determines what types are used. Here's an example of a generic class:
public class Foo<T extend CharSequence, U> {
private List<T> listField;
private U objField;
public void bar(List<? extends T> listParam) {
// method body...
}
public U baz(T objParam) {
// method body...
}
public <V extends Number> V qux(V objParam) {
// method body...
}
}
And following the aforementioned rules of type erasure, here's what the above class looks like afterwards:
// the raw type of Foo
public class Foo {
private List listField;
private Object objField;
public void bar(List listParam) {
// method body...
}
public Object baz(CharSequence objParam) {
// method body...
}
public Number qux(Number objParam) {
// method body...
}
}
But again, you'll only need to know about the latter version when you're using raw types.
Applying This Knowledge to Your Question
Some of what we've learned so far is that wildcards can only be used in type arguments and are thus only applicable to parameterized types. The erasure of a parameterized type is simply the raw type. If we apply this knowledge to your examples you get the following:
Example #1
Example #2
Example #3
Example #4
Reinforcing the Point
To really point out the difference between the erasure of a type variable and the erasure of a parameterized type let's look at another example.
public class Foo<T extends Number> {
public void bar(T obj) {
// method body...
}
public void baz(List<? extends T> list) {
// method body...
}
}
The method bar
has a single parameter of type T
. The parameter is using the type variable directly. The erasure of a type variable is the erasure of its leftmost bound which is Number
in this case. This means after erasure the method's parameter is Number
.
The method baz
has a single parameter of type List<? extends T>
. Here, the type variable T
is being used as the upper bound in the type argument of a parameterized type. In other words, despite a type variable being used, the erasure actually being used here is that of a parameterized type. This means after erasure the method's parameter is just List
. This would happen even if the type argument was a lower-bounded wildcard (e.g. List<? super T>
), an unbounded wildcard (e.g. List<?>
), or even a non-wildcard (e.g. List<T>
).
How Wildcards are Handled by Type Erasure
To directly answer your question on how type erasure treats wildcards, the answer is effectively: It doesn't, not directly. Wildcards simply disappear (when the parameterized type is erased) and have no significance in the resulting raw type.
Wildcards enable flexibility for those using the generic API. Here are some Q&As which address that concept:
Hopefully those Q&As also help answer your auxiliary question, which is what are the differences between the second and fourth examples from your post.