В Java, почему массив не может быть переменной типа Variable, но может быть подстановочной рамкой?

В Java, почему массив не может быть переменной типа Variable, но может быть привязкой подстановочного знака?

У вас может быть:

List< ? extends Integer[] > l;

но вы не можете:

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

Почему?

Ответ 1

Рассмотрим этот код Java:

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});
    }
}

Для вашего первого случая:

List< ? extends Integer[] > l;

Когда вы делаете что-то вроде этого List< ? extends Integer[] > l;, тогда компилятор Java видит его как List< ? extends Object> l; и переводит его соответствующим образом. Вот почему вы не получаете никаких ошибок.

Сгенерированный байт-код выглядит следующим образом:

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

Проверьте номер строки 21. Хотя, я прошел массив java.lang.Integer; внутренне он переводится на java.lang.Object.

Для вашего второго случая:

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

В соответствии со спецификацией языка java:

TypeParameter:
TypeVariable TypeBoundopt

TypeBound:
extends ClassOrInterfaceType AdditionalBoundListopt
.
.

Как вы можете видеть, граница состоит только из класса или интерфейса (даже не примитивных типов). Поэтому, когда вы делаете что-то вроде этого class MyClass< T extends Integer[] > { }, тогда Integer[] не квалифицируется как класс или интерфейс.

В соответствии с моим пониманием Java Spec это было сделано для решения всех сценариев, таких как

  • class MyClass< T extends Integer[] >
  • class MyClass< T extends Integer[][] >
  • ..
  • class MyClass< T extends Integer[][]...[] >

Поскольку все они могут быть представлены как java.lang.Object и переданы как параметр, как в примере

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

как 'c' запоминает свой истинный тип.

Надеюсь, это поможет.

Ответ 2

Я пытаюсь думать о конкретных причинах, по которым это должно быть запрещено, но единственное, о чем я могу думать, это то, что это абсолютно ненужная конструкция, потому что:

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

эквивалентно

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

Очевидно, то же самое нельзя сказать о корпусе шаблона, следовательно, это разрешено.

Ответ 3

Вы можете сделать что-то вроде:

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

Или вы можете сделать что-то вроде этого:

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

Надеюсь, это поможет, и я не слишком далеко от того, о чем вы просили.