We've now talked about relationships between generic types and even between generic types and raw types. But we haven't brought up the concept of a cast yet. No cast was necessary when we interchanged generics with their raw types. Instead, we just crossed a line that triggers unchecked warnings from the compiler:

Normally, we use a cast in Java to work with two types that could be assignable. For example, we could attempt to cast an Object to a Date because it is plausible that the Object is a Date value. The cast then performs the check at runtime to see if we are correct. Casting between unrelated types is a compile-time error. For example, we can't even try to cast an Integer to a String. Those types have no inheritance relationship. What about casts between compatible generic types?

This code snippet shows a valid cast from a more general Collection<Date> to a List<Date>. The cast is plausible here because a Collection<Date> is assignable from and could actually be a List<Date>. Similarly, the following cast catches our mistake where we have aliased a TreeSet<Date> as a Collection<Date> and tried to cast it to a List<Date>:

Here, we aliased an ArrayList<String> as a plain Object. Next, we cast it to a List<Date>. Unfortunately, Java does not know the difference between a List<String> and a List<Date> at runtime, so the cast is fruitless. The compiler warns us of this by generating an unchecked warning at the location of the cast; we should be aware that we might find out later when we try to use the cast object that it is incorrect. Casts are ineffective at runtime because of erasure and the lack of type information.