1

I am studying "Java in a Nutshell" 5th Edition. In p169, it says "Remember, though, that type variables exist only at compile time, so you can’t use a type variable with the runtime operators instanceof and new.". If I understand it correctly (Am I??), new and instanceof should not be used with type variables. But after I did some tests, I became more confused.

  1. List< ?> l = new ArrayList< ?>(); // not compiled and I may know why
  2. List< E> l = new ArrayList< E>(); // compiled!! WHY??

Could anyone tell me why (2) is allowed??

Really thanks for any information.

3 Answers3

7

The statement "you can't use type variable with new" means you can't do this:

E e = new E(); // can't do this

But you can pass a type variable through to another class for it to use:

class MyClass<E> {
    List<E> = new ArrayList<E>(); // OK
}

You're not instantiating E, you're instantiating an ArrayList with the type E.

Regarding using ?, that's a different issue. The ? means "unknown" type, while a variable can be of an unknown type, you can't instantiate a generic class by specifying unknown type - you must pass a known type through to the generic class.

You can however code this:

List<?> list = new ArrayList<E>();
Bohemian
  • 412,405
  • 93
  • 575
  • 722
2

Unless you've defined it, E isn't a class.

E is just a way to make reference to a class during compilation. That way, proper methods can be called with it.

List<String> list1 = new ArrayList<String>(); // Diamond types for Java 7+
List<Integer> list2 = new ArrayList<Integer>();

// Populate lists

list1.get(0).charAt(0); // Perfectly fine, String#charAt(int) exists
list2.get(0).charAt(0); // Error! Integer doesn't have a charAt(int) method!

So, the type E inside the ArrayList class is used to ensure that you can perform operations on the elements if you know the type, as if it were that type. This avoids running into issues when calling on methods that only exist in the specific class, as I showed with the charAt(int) method.

Adding onto this, it can be pointed out why you can't make the call with the ?.

List<?> list1 = new ArrayList<String>(); // Fine
List<?> list2 = new ArrayList<?>();      // What type is this list composed of?

It doesn't make sense to make a list of an unknown type. If you want a list of a generic type, you can always pass the Object into the generics, but there was no reason to add support for creating instances with an unknown type.

Obicere
  • 2,999
  • 3
  • 20
  • 31
0

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 Strings 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.

Community
  • 1
  • 1
yshavit
  • 42,327
  • 7
  • 87
  • 124
  • To be fair, the following code compiles but explodes at runtime if there's no no-args constructor: `MyClass x = MyClass.class.newInstance();`, so that's not the reason why you can't code `new E();` – Bohemian Apr 09 '14 at 05:45
  • @Bohemian True, that negates the "ensure that a no-arg constructor exists" part. But the "how else can it call the right constructor" bit still holds, and that's the more important bit (though I failed to identify it as such in my answer). – yshavit Apr 09 '14 at 05:53