Подтвердить что ты не робот

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

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

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

public class Hash<T extends Comparable<String>> {
    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. Есть ли способ обойти это?

4b9b3361

Ответ 1

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

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

private Comparable[] hashtable;

...

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

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

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

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

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

List<String> list = new ArrayList<String>();
List<Integer> list2 = (List<Integer>)list;
list.add(3);

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

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

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

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

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

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

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

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

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

Нет, поскольку он не безопасен для типов.

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

...

Ответ 2

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

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

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

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

Ответ 3

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

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

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

T extends Comparable<String>

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

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

Ответ 4

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

public class Hash<T extends Comparable<String>>
{
  Hash(int records, double load, Class<T> class)
  {
    tableSize = (int)(records / loadFactor);
    tableSize = findNextPrime(tableSize);

    hashTable = java.lang.reflect.Array.newInstance(class, tableSize);
  }

private T[] hashTable;
private int tableSize;

}

Ответ 5

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

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

Item<K>[] array = new Item[SIZE];

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

private K value;

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