2015-09-15 2 views
2

Я очень новичок в родовом программировании на Java.Почему это неправильно для создания массива Object []?

Я не понимаю, почему массивы родового типа не могут быть созданы.

T[] a = new T[size]; //why does this give an error? 

Если общий тип означает, что общий заполнитель T будет заменен на имя класса во время выполнения, что мешает нам создать массив, имеющий общие ссылки?

После недолгих поисков, я нашел обходной путь

T[] a = (T[])new Object[size]; //I don't get how this works? 

Хотя я нашел решение, я до сих пор не понимаю, что мешает созданию общего массива. Предположим, что я создаю функцию, которая возвращает массив объектов.

public Object[] foo(){ 
     return new Object[12]; 
} 

А затем сделать вызов

String[] a = (String[])foo(); 

дает ClassCastException. Но почему? Не похоже ли это на первую строку кода, в которой я передал массив Object в массив T?

T[] a = (T[])new Object[size]; 

Если это прошло без сбоев, почему не сделал этого?

+7

Вы спросили в общей сложности шесть вопросов в один :) –

+0

@SURESH АТТА Я чувствовал, что все они были связаны:/ –

+0

@Djack: generic type ** не означает ** «что общий заполнитель T будет заменен именем класса во время выполнения». Он заменяется ** до ** времени выполнения «Объектом». Генераторы Java работают иначе, чем шаблоны C++. –

ответ

3

Часть вопроса - смотреть на нее наоборот. Вы не можете сделать (String[]) new Object[10]; потому что массив Object равен не a String массив. Поскольку

String[] array = new String[10]; 
array[0] = "foo"; 
String foo = array[0]; 

прекрасно, но

Object[] objectArray = new Object[10]; 
objectArray[0] = 10; 
String[] stringArray = (String[]) objectArray; 
String foo = stringArray[0]; 

... пытается присвоить Integer к String, что не должно быть разрешено в первую очередь. Таким образом, этот код выходит из строя, когда вы набрасываете Object[] на String[]. В этом коде есть, чтобы выбросить ClassCastException.

Это все равно для Java еще до того, как вначале были изобретены дженерики. Примите все это первые. Затем перейти к дженерикам.

Теперь, способ Java генерики реализованы означает, что при компиляции кода, T молча переписана Object. Таким образом, T[] array = (T[]) new Object[10] молча разрешен, потому что он фактически переписывается на Object[] array = new Object[10]. Но как только вы это вынимаете, все идет не так. Например,

private static <T> T[] newArray() { 
    return (T[]) new Object[10]; 
} 

если вы звоните String[] array = newArray(), вы получите ClassCastException на месте вызова, а не в newArray(). Вот почему Java дает вам предупреждение в (T[]) new Object[10], и это предупреждение вполне может привести к настоящему ClassCastException.

Вообще говоря, не смешивайте массивы и дженерики. Путь вокруг всего этого - правильно использовать List.

+0

Ваш ответ был полезен, но если 'T' переписан на' Object' то когда я создаю экземпляр, что происходит? Как 'Hello = new Hello <>()' Остается ли 'T []' оставаться 'Object []', а не стать 'String []'? –

+0

Да. Да, 'T []' остается 'Object []' only, а затем, когда вы пытаетесь получить его как 'String []', вы получаете 'ClassCastException'. Java будет вставлять приемы типов везде, где вы вынимаете дженерики и назначаете их фактическому типу. –

+0

Итак, если во время компиляции общие заполнители переписываются в «Object», то «T [] = new T [size]» не должно быть проблемой, потому что оно будет преобразовано в 'Object [] = new Object [size]' , Но все же компилятор дает ошибку? –

3

При работе с массивами необходимо отметить несколько вещей.

Во-первых, массивы считаются covariant; то есть типизированный массив будет поддерживать свою цепочку наследования. Таким образом, Integer[] является Object[] таким же образом, что Integer является Object.

Вот почему ваш последний пример не удается. Вы хотите, чтобы бросить Object[] к String[] через foo:

String[] a = (String[])foo(); 

Object[] никогда не будет String[] так как Object не String (а наоборот всегда будет истинным).

Во-вторых, массивы и дженерики не смешивают все это хорошо. Дженерики считаются инвариантом; то есть они не будут поддерживать свои сети наследования. A List<Integer>: не считается таким же, как List<Object>.

Что касается причины вашего конкретного примера, это связано с стиранием типа во время компиляции. Массивы должны знать свой конкретный тип во время компиляции, и без этой информации они не могут быть созданы. Поскольку generics не хранят эту информацию, вы не можете создавать экземпляр общего массива так же, как вы создавали бы не общий набор.

То есть, вы должны использовать форму произнесения:

T[] a = (T[]) new Object[size]; 

You can read a bit more about generic arrays in this answer, так как она охватывает большинство основных моментов, которые вы должны были бы знать, при общении с ними.

+0

Вы сказали: «... нужно знать их конкретный тип во время компиляции, и без этой информации они не могут быть созданы». Но экземпляры не выполняются во время компиляции, но время выполнения и во время выполнения мы знаем конкретный тип для и затем во время выполнения он может создать экземпляр массива этого конкретного типа. Пожалуйста, поправьте меня, если я ошибаюсь. – vvtx

+0

@vvtx: Да, вы ошибаетесь. Вы не можете подтвердить общий параметр. Массивы требуют повторяемости. – Makoto

+0

Да, я узнал, что массивы требуют повторяемости, но это требуется во время создания экземпляра, то есть во время выполнения, и во время выполнения вы знаете свой конкретный тип. Вы сказали: «Вы не можете подтвердить общий параметр», но когда тип параметра не является массивом, он переопределяется в тип объекта в байтоде, т. Е. T e заменяется на Object e после компиляции, а затем останавливает T [] e на Object [] e? – vvtx

0

Массивы знают свой тип во время выполнения. A String[] знает, что это массив из String s.

В отличие от этого, параметры типового типа стираются во время выполнения, поэтому List<String> во время выполнения является только List.

Поскольку параметры типа T не доступны во время выполнения new T[10] (который не компилируется), невозможно создать истинный T[].

Это не правда, что

T[] a = (T[])new Object[size]; 

не может бросить исключение. Оно может. Пример Луи Вассермана показывает, что он может вызвать исключение на сайте вызова, но эта строка также может напрямую передавать исключение.Например

public static void main(String[] args) { 
    foo(); 
} 

static <T extends Number> void foo() { 
    T[] array = (T[]) new Object[42]; 
} 

Здесь, нижняя граница T является Number, поэтому во время выполнения, он пытался бросить Object[] к Number[], который бросает ClassCastException.

Вы можете создать T[], если у вас есть Class<T> объект clazz, используя, например,

Array.newInstance(clazz, length); 
Смежные вопросы