Общие массивы в Java

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

Мне нужно создать массив дженериков. Но сам общий тип расширяет Comparable. Когда я попробую следующее:

public class Hash<T extends Comparable> { private T[] hashTable; private int tableSize; Hash(int records, double load) { tableSize = (int)(records / loadFactor); tableSize = findNextPrime(tableSize); hashTable = (T[])(new Object[tableSize]); //Error: Ljava.lang.Object; cannot be cast to [Ljava.lang.Comparable; } } 

Проблема в том, что объект нельзя отличить как общий, который расширяет Comparable. Есть ли способ обойти это?

В общем случае генераторы и массивы не смешиваются. Короткий ответ заключается в том, что вы можете обойти эту проблему. Более длинный ответ заключается в том, что вы, вероятно, не должны, и я объясню, почему.

Вы можете использовать Array.newInstance() следующим образом:

 private Comparable[] hashtable; ... hashtable = (Comparable[])Array.newInstance(Comparable.class, tableSize); 

но вы не можете создать массив вашего параметризованного типа.

Массивы ковариантны . Это означает, что они сохраняют тип своих элементов во время выполнения. Генералов Java нет. Они используют стирание типа, чтобы в основном замаскировать неявное литье, которое происходит. Это важно понять.

Поэтому, когда вы создаете массив Object, вы не можете использовать его, скажем, для массива Comparable (или любого другого типа), потому что это неверно.

Чтобы привести пример. С дженериками это совершенно законно:

 List list = new ArrayList(); List list2 = (List)list; list.add(3); 

Вот почему вы не можете этого сделать:

 public  T newInstance(T t) { return new T(); // error! } 

т.е. во время выполнения нет знаний о classе Т. Вот почему приведенный выше код чаще всего записывается как:

 public  T newInstance(T t, Class clazz) { return clazz.newInstance(); } 

потому что их нет типа времени выполнения для общего аргумента. Но с массивами:

 String arr[] = new String[10]; Integer arr2[] = (Integer[])arr; // error! 

То, что вы должны делать в этом случае (imho), не использует массивы, а использует ArrayList . Честно говоря, очень мало оснований для использования массивов над ArrayList и дженерики – всего лишь один пример этого.

Для лучшего и более полного объяснения см. (Отличные) часто задаваемые вопросы Java Generics :

Могу ли я создать массив, тип компонента которого является конкретным параметризованным типом?

Нет, потому что это не безопасно.

Массивы являются ковариантными, что означает, что массив ссылок на супертипы является супертипом массива ссылок подтипов. То есть Object[] является супертипом String[] и массив строк можно получить через ссылочную переменную типа Object[] .

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

 hashTable = (T[])(new Comparable[tableSize]); 

(т. е. создать массив типа raw Comparable вместо Object)

Если вы правильно инкапсулируете весь доступ к этому массиву внутри вашего объекта Hash, это должно работать, но (как объясняют другие ответы) вы можете оставить себя уязвимыми.

Приведение, которое вы пытаетесь

 (T[])(new Object[tableSize]); 

fail, потому что элементы в массиве являются экземплярами Object. Объект не расширяет Comparable , поэтому приведение (T []) не выполняется, поскольку T определяется как:

 T extends Comparable 

Чтобы решить эту проблему, выполните следующие действия:

  • Создайте массив таким образом, чтобы его элементы были экземплярами некоторого classа, которые расширяют Comparable
  • Измените hashTable из массива (который не является общим типом), в общий тип коллекции, например List hashTable = new ArrayList(tableSize>)

Вы часто сталкиваетесь с проблемами, когда вам нужно создать экземпляр какого-то родового типа. Самый простой способ обойти это – передать class, который фактически будет храниться в конструкторе. Таким образом, вы можете построить из фактического типа. Попробуйте что-то вроде этого:

 public class Hash> { Hash(int records, double load, Class class) { tableSize = (int)(records / loadFactor); tableSize = findNextPrime(tableSize); hashTable = java.lang.reflect.Array.newInstance(class, tableSize); } private T[] hashTable; private int tableSize; } 

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

Однако этот неявный бросок отлично работал:

 Item[] array = new Item[SIZE]; 

где Item – это class I, содержащий член:

 private K value; 

Таким образом вы получите массив типа K (если элемент имеет только значение) или любой общий тип, который вы хотите определить в элементе classа.

  • В чем преимущество linspace над двоеточием: «оператор?
  • Как удалить определенный элемент из JSONArray?
  • Передача многомерного массива переменной длины в функцию
  • Как GCC реализует массивы переменной длины?
  • сортировка 2D-массива String в java
  • c ++ array - выражение должно иметь постоянное значение
  • Java: каково большое время для объявления массива размера n?
  • jQuery .inArray () всегда верно?
  • Получить объект JavaScript из массива объектов по значению свойства
  • Смутно о объявлениях Swift Array
  • как разрешить массив с сильными параметрами
  • Interesting Posts
    Давайте будем гением компьютера.