2013-02-11 3 views
2

У меня есть следующий кодJava Есть ли способ инициализировать универсальный массив, тип которого безопасен?

public class Container<T> { 
    private T element; 
    private T[] tarray; 

    public T getElement() { 
     return element; 
    } 

    public void setElement(T element) { 
     this.element = element; 
    } 

    public void add(T element) { 
     tarray[0] = element; 
    } 

    public void CreateArray(int size) { 
     //Can this be cleaned up better? 
     tarray = (T[]) new Object[size]; 
    } 

    public T get() { 
     return tarray[0]; 
    } 

    public Container(T someElement) { 
     this.element = someElement; 
    } 

    public Container() { 
    } 

    public static void main(String[] args) { 
     Container<String> myContaier1 = new Container<String>(); 
     myContaier1.setElement("Hello"); 
     myContaier1.CreateArray(1); 
     myContaier1.add("GoodBye"); 
     System.out.println(myContaier1.get()); 
    } 
} 

Нет ли способ инициализации типа безопасного общего массива?

+1

Duplicate of http://stackoverflow.com/questions/529085/java-how-to-generic-array-creation – Aubin

+0

Это эффективно Object [] (как и все стандартные контейнеры, такие как ArrayList), поэтому угрожайте ему как одному , – bestsss

ответ

6

Там нет никакого способа, если вы не предоставите овеществленным T в виде фактического Class<T> объекта, который представляет собой конкретное значение T. Это связано с тем, что тип массива равен, а тип Generic - нет.

+2

Интересное слово "reified". Что это значит? Является ли это специальным лексиконом Generics? – AlexWien

+0

@AlexWien Это общий термин, который также относится к обсуждению «Дженерики». Это означает, что время выполнения может наблюдать за ним, в отличие от только компилятора. Это означает, что вы можете обратиться к нему, передать его в качестве аргумента, вернуть метод, сохранить его в переменной и т. Д. –

-1

Вы можете сделать private T[] tarray;, но вы не можете назначить его (T[]) new Object[size];. Как массив объектов может быть одним и тем же массивом любого другого класса. T нет нет после сбор. Это называется стиранием типа. Например, если вы

List<Person> persons = new ArrayList<Person>(); 

Это становится List persons = new ArrayList() после компиляции.

+2

Нет ничего плохого в поле 'private T [] tarray;' – newacct

+0

@newacct Ответ обновляется – fastcodejava

+0

Что значит «невозможно назначить». Как вы думаете, что произойдет, если вы присвойте '(T []) новый Object [size];' to 'tarray'? – newacct

1

Есть две проблемы:

Прежде всего, фактический тип вашего массива всегда будет Object[]. Вы отбрасываете его до T[], но это работает только потому, что T[] стирает до Object[]. Если определение вашего класса говорит, например, <T extends Number>, то (T[])new Object[] потерпит неудачу с ClassCastException.

Вы можете обойти эту проблему, передавая Class<T> конструктору вашей коллекции и держать его в поле:

private Class<T> componentClass; 
... 
tarray = (T[]) Array.newInstance(componentClass, size); 

Сейчас актуально в памяти типа tarray является T[], но вы все равно получите непроверенный ошибка заливки. Несмотря на то, что у вас есть класс компонента, насколько я знаю, нет эквивалента Class.cast() для выполнения проверенного экземпляра экземпляра массива.

+0

«Если ваше определение класса говорит, например, , то (T []) новый Object [] завершится с исключением ClassCastException." Правильно, но тогда в этом случае это будет '(T []) новый Number []'. В принципе, любой T [] стирается. – newacct

-1

Это похоже на запрос «Есть ли способ инициализировать общий объект: new T()

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

Array - это то же самое, его тип зависит от его элементов. Если тип его элементов неизвестен, сам тип неизвестен.

Эти классы, которые могут принимать общие типы, такие как List, Set, Map и т. Д. Разные. У них свои классы как типы, и они динамичны, поэтому вы можете инициализировать один, например, new ArrayList<T>().


Вы можете попробовать на этом:

public class Test { 
    public static void main (String args[]) { 
     int[] i = new int[3]; 
     short[] s = new short[4]; 
    ArrayList<String> a = new ArrayList<String>(); 

     System.out.println(i.getClass().getName()); 
     System.out.println(s.getClass().getName()); 
     System.out.println(args.getClass().getName()); 
     System.out.println(a.getClass().getName()); 
    } 
} 

Вы увидите типы элементов уже в сочетании с массивами, а не в сочетании с ArrayList.

-1

Есть способ сделать это на JVM, но для того, чтобы сделать что-то подобное на Java, потребуется написать много кода плиты котла. В Scala вы можете использовать Manifests, чтобы обойти стирание стилей и создавать экземпляры универсальных массивов без кастинга.

Пример:

class Container[T : Manifest]() { 
    def createArray(size:Int) = Array.ofDim[T](size) 
} 

scala> val f = new Container[String] 
f: Container[String] = [email protected] 

scala> f.createArray(5) 
res7: Array[String] = Array(null, null, null, null, null) 

Scala код компилируется в том же классе байткодом файлов как Java (подразумевая, что это должно быть возможно в Java, если вы прыгали через достаточно обручи и, возможно, написал свои манифесты). Классы Scala могут быть импортированы в Java-проекты ... хотя я не уверен, как сложно создать экземпляр класса, подобного этому с Java.

Если вы часто оказываетесь в состоянии справиться с ситуациями более сложного типа, взгляните на Scala, вам может понравиться.

Смежные вопросы