11

In Java, why can't an array be a Type Variable's bound, but can be a Wildcard's bound?

You can have:

List< ? extends Integer[] > l;

but you can't have:

class MyClass< T extends Integer[] > { } // ERROR!

Why?

Qantas 94 Heavy
  • 15,750
  • 31
  • 68
  • 83
John Assymptoth
  • 8,227
  • 12
  • 49
  • 68
  • It's a strange one. The JLS quite clearly states that you can only use classes or interfaces in type variables but fails to give any explanation whatsoever. – biziclop Jan 23 '11 at 19:22
  • just to do like-comparisons: "List< ? extends Integer[] > k1;" is allowed, but "List< T extends Integer[] > k2;" is not. But I couldnt find anything except null to add to the list! Eclipse says k1.get(0) returns type Integer[], but I could not add one. – david van brink Jan 23 '11 at 19:43

3 Answers3

7

Consider this Java code:

package test;

public class Genric<E>
{
    public Genric(E c){
        System.out.println(c.getClass().getName());
    }   
    public static void main(String[] args) {
        new Genric<Integer[]>(new Integer[]{1,2});
    }
}

For your first case:

List< ? extends Integer[] > l;

When you do something like this List< ? extends Integer[] > l; then the Java compiler sees it as a List< ? extends Object> l; and translates it accordingly. So this is why you don't get any error.

The generated byte-code is as follows:

   .
   .
   .
   20:  aastore
   21:  invokespecial   #52; //Method "<init>":(Ljava/lang/Object;)V
   24:  return
   .
   .

Checkout the line number 21. Although, I have passed an array of java.lang.Integer; internally it is translated to java.lang.Object.

For your second case:

class MyClass< T extends Integer[] > { } // ERROR!

As per java language specification:

TypeParameter:
TypeVariable TypeBoundopt

TypeBound:
extends ClassOrInterfaceType AdditionalBoundListopt
.
.

As you can see the the bound consists solely of class or an interface (not even primitive types). So when you do something like this class MyClass< T extends Integer[] > { } then Integer[] does not qualify as a class or interface.

As per my understanding of Java Spec, this was done to solve all the scenarios like

  1. class MyClass< T extends Integer[] >
  2. class MyClass< T extends Integer[][] >
  3. ..
  4. class MyClass< T extends Integer[][]...[] >

Because all of them can be represented as java.lang.Object and when passed as parameter, as in example

public Genric(E c){
            System.out.println(c.getClass().getName());
        }

as 'c' remembers its true type.

Hope this will help.

Favonius
  • 13,959
  • 3
  • 55
  • 95
  • +1 for digging into ther bytecode, although this still is more like a an explanation of "hows" rather than the "whys". Isn't the replacement of the type parameter with `Object` simply due to type erasure, and applied always? I.e. `List` would also become a `List` in the bytecode isn't it? Generic type safety is checked only by the compiler before the type erasure happens. – Péter Török Jan 23 '11 at 21:21
3

I'm trying to think about specific reasons this should be forbidden but the only one I can think of is that it's a completely unnecessary construct, because:

class Foo<T extends Integer[]> {
   T bar();
}

is equivalent to

class Foo<T extends Integer> {
   T[] bar();
}

Obviously the same cannot be said about the wildcard case, hence it's allowed there.

biziclop
  • 48,926
  • 12
  • 77
  • 104
0

You can do something like:

public class MyClass<K> {
    K array;
    public MyClass(K k) {
       array = k;
    }

Or you could do something like this:

class MyClass< T extends Integer > {
   ...make your variable MyClass[]...
}

Hope this helps and I'm not too far off what you were asking for.