2016-11-08 2 views
0

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

public class A <T>{ 

    private T [] datas; 
    // more code here ... 
} 

И я хочу, чтобы воспользоваться конструктором, чтобы инициализировать массив. Предположим, что у меня есть следующий конструктор

public A(T element){....} 

Java не позволяет мне использовать что-то вроде

datas = new T[10] 

И будет жаловаться, что я не может создать общий массив T Но я могу еще используйте работу, например:

@SuppressWarnings("unchecked") 
    public A(T element){ 

     List<T> datasList = new ArrayList<T>(); 
     datasList.add(element); 
     datas =(T[]) datasList.toArray(); 
    } 

У меня есть предупреждение от компилятора, поэтому мне пришлось добавить @Suppress Предупреждения, но моя точка связана со следующим комментарием от документации метода ToArray (Пожалуйста, обратите внимание на рисунок)

enter image description here

Это говорит о том, возвращаемый массив является безопасной. Значит ли это, что безопасно использовать этот метод? Если не так? И что было бы лучшим способом сделать такую ​​инициализацию в конструкторе? Я также хотел бы рассмотреть случай переменного списка элементов T в перегруженном конструкторе, таком как

public A (T ... elements) {....}.

+2

Вам не обязательно использовать список, но можете попробовать '(T []) Array.newInstance (element.getClass(), 10)'. Это все равно будет генерировать предупреждение из-за приведения, но вы можете смело игнорировать это, если 'element.getClass()' is 'Class ' (который должен быть в большинстве случаев - если 'T' не определяется чем-то другим и 'element' будет подклассом определенного' T'). – Thomas

+0

@Thomas спасибо. это будет трюк. – alainlompo

+0

Хороший вариант, я не думал об этом. Прокомментировал комментарий –

ответ

2

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

public A(T element){ 
    int length = 10; 
    datas = (T[])Array.newInstance(element.getClass(), length); 
} 

Однако, есть проблема, если element будет подклассом от T, например если вы назвали бы это так:

A<Number> numberA = new A<>(Integer.valueOf(1)); 

Здесь T бы Number но класс element будет Integer. Чтобы уменьшить это, вы можете передать массив vararg типа T, например. как это:

//firstElement only exists to force the caller to provide at least one element 
//if you don't want this then just use the varargs array 
A(T firstElement, T... furtherElements){  
    int length = 10; 
    Class<?> elementClass = furtherElements.getClass().getComponentType();  
    datas = (T[])Array.newInstance(elementClass, length); 
} 

Поскольку переменные аргументы всегда приводят в массив (даже длины 0) вы получите массив типа T и может получить тип компонента этого.

Таким образом, в случае с выше numberA.datas будет массив Number[], а не массив Integer[].

+0

Хороший вопрос о подклассе, но в этом случае я бы предложил добавить «Класс < T > clazz» вместо T firstElement –

+0

@JeroenvanDijk да, это было бы самым безопасным способом, но для этого требовалось бы другое api (что может быть трудно изменение). – Thomas

+0

True (снова) :) –

0

Вы можете передавать дженерики, но вы не можете позвонить новым T (or new T[ ]). Имейте в виду, что после компиляции дженериков нет, поэтому на самом деле это помогает только при написании кода. Зная, что он ушел во время работы, также очевидно, что новый T() нельзя назвать общим, T удаляется во время выполнения.

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

Более удобный способ (imho) - создать статический метод, поскольку он является чисто входным -> выходным. Вы должны объявлять дженерики перед типом метода возвращения:

public <T> T[ ] toArray(T... objects) { ... } 
Смежные вопросы