4

I don't understand why the following happens with Java Generics if no type is specified. Let's say we have a simple class:

public class NumberList<T extends Number> extends ArrayList<T> {        
}

Then, this works fine:

NumberList<Number> list = new NumberList<Number>();
list.add(5d);
list.add(2f);
for (Number n: list) {
  System.out.println(n);
}

But, if I don't specify the type, it doesn't.

NumberList list = new NumberList();
list.add(5d);
list.add(2f);
for (Number n: list) { // Error: Change type of n to Object
  System.out.println(n);
}

Why the list iterator now returns Objects? Shouldn't it somehow default to the 'lowest' class (Number)? Can I force it to do that if nothing is specified?

Lovro
  • 712
  • 1
  • 10
  • 20
  • 1
    `x instanceof Object` is true for any class. I would guess the type signature is similar (if not completely equivalent to) `` – vikingsteve Apr 01 '16 at 17:33

2 Answers2

4

NumberList is a raw type. Raw types should not be used and are only still allowed for compatibility with code written before generics were introduced into the language.

Raw types work by replacing all types in method arguments and return types by their erasure. If T extends Number, the erasure of T is Number, but crucially all type information in <> is simply ignored by the erasure process. In this case, list acts as if iterator() returns plain old Iterator rather than Iterator<T> (remember that an enhanced for loop is really just syntactic sugar for using an Iterator).

Interestingly, the following does compile:

NumberList list = new NumberList();
list.add(5);
Number a = m.get(0);

This is because get returns T and the erasure of T is Number. You might expect that if if T is treated as Number, then Iterator<T> ought to get treated as Iterator<Number>, but since we are simply advised to never use raw types, it made sense to keep the rules as simple as possible (even if they are a little strange).

The standard question on this topic is this.

Community
  • 1
  • 1
Paul Boddington
  • 37,127
  • 10
  • 65
  • 116
1

NumberList is a raw type, and raw types are contagious. If you reference a raw type, then all of its methods also work on raw types.

In particular, that means that the Iterator<T> iterator() method that the for loop relies on (based on NumberList being Iterable) is becomes Iterator iterator() — it returns a raw Iterator type. And the erasure for Iterable is Object, not Number.

In other words, think about how this looks from the for's perspective. With generics:

Iterable<Number> implicitIterable = list;
for (Number n: implicitIterable) ...

With raw types:

Iterable implicitIterable = list;
for (Number n: implicitIterable) ... // error! implicitIterable.next()
                                     // returns Object, not Number
yshavit
  • 42,327
  • 7
  • 87
  • 124