Bohemian answered a bit of the "what," but I'd like to take a crack at some of the "why."
Let's start with what you can't do. It all comes down to erasure, which basically means that when your code has a List<String>
, the bytecode information only has a List
. Anything between the angle brackets is missing from the bytecode (and thus from the runtime).
So, why can't you do foo instanceof E
? Well, because that's a runtime check, and E
isn't known at runtime.
And why can't you do new E()
? Again, because that requires the runtime to know what E
is (how else can it call the right constructor? Or even ensure that a no-arg constructor exists?), but that information isn't available to it.
In all these cases, the thing to keep in mind is that the language isn't preventing you from doing something just for the sake of preventing you. It's preventing you from doing it because it'd be impossible to execute at runtime.
So, why can you type new ArrayList<String>()
? Because the only information that the runtime needs for that is the fact that an ArrayList
is being instantiated, and it has that information. The fact that it's an ArrayList
of String
s doesn't matter to the runtime, because erasure means that the ArrayList
doesn't know or care about what it's a list of, at runtime.