Search code examples
javaarrayswildcardboundstype-parameter

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


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?


Solution

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