public void put(int index, T item) { array[index] = item;
}
@SuppressWarnings("unchecked")
public T get(int index) { return (T)array[index]; }
@SuppressWarnings("unchecked")
public T[] rep О {
return (T[])array; // Предупреждение: непроверенное преобразование
}
public static void main(String[] args) { GenericArray2
new GenericArray2
gai.put(i, i): for(int i = 0: i < 10; i ++)
System.out.print(gai.get(i) + " "); System.out.printlnO; try {
Integer[] ia = gai.rep(); } catch(Exception e) { System.out.printin(e); }
}
} /* Output: (Sample)
0 12 3 4 5 6 7 8 9
java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer,
На первый взгляд почти ничего не изменилось, разве что преобразование типа было перемещено. Без директив @SuppressWarnings вы по-прежнему будете получать предупреждения, но теперь во внутренней реализации используется Object[] вместо Т[]. При вызове get() объект преобразуется к Т; это правильный тип, поэтому преобразование безопасно. Но при вызове гер() снова делается попытка преобразования Object[] в Т[], которое остается неверным; в результате вы получите предупреждение во время компиляции и исключение во время выполнения. Не существует способа обойти тип базового массива, которым может быть только Object[]. У внутренней интерпретации array как Object[] вместо Т[] есть свои преимущества: например, вы с меньшей вероятностью забудете тип массива, что приведет к случайному появлению ошибок (впрочем, подавляющее большинство таких ошибок будет быстро выявлено на стадии выполнения).
В новом коде следует передавать метку типа. В обновленной версии Generic-Array выглядит так:
//: generics/Generic/\rrayWithTypeToken.java
import java.lang.reflect.*;
public class GenericArrayWithTypeToken
public GenericArrayWithTypeToken(CIass
}
public void put(int index, T item) { arrayCindex] = item;
}
public T get(int index) { return arrayCindex]; } // Expose the underlying representation: public T[] rep() { return array; } public static void main(String[] args) {
GenericArrayWithTypeToken
new Generi cArrayWi thTypeToken
// This now works:
Integer[] ia = gai.rep();
}
} ///
Метка типа Class
К сожалению, просмотрев исходный код стандартных библиотек Java SE5, вы увидите, что преобразования массивов Object в параметризованные типы происходят повсеместно. Например, вот как выглядит копирующий конструктор для создания ArrayList из Collection после некоторой правки и упрощения:
public ArrayList(Collection с) { size = c.sizeO;
elementData = (E[])new Object[size]; с.toArray(elementData):
}
В ArrayList.java подобные преобразования встречаются неоднократно. И конечно, при их компиляции выдается множество предупреждений.
Ограничения
Поскольку стирание уничтожает информацию о типе, при отсутствии ограничений для параметров типов могут вызываться только методы Object. Но, если ограничить параметр подмножеством типов, вы сможете вызвать методы из этого подмножества. Для установления ограничений в Java используется ключевое слово extends. Важно понимать, что в контексте параметризации extends имеет совершенно иной смысл, нежели в обычной ситуации. Следующий пример демонстрирует основы установления ограничений:
//: generics/BasicBounds.java
interface HasColor { java. awt. Col or getColorO; }
class Colored
Colored(T item) { this.item = item; }
T getltemO { return item; }
// Ограничение позволяет вызвать метод:
java. awt. Col or colore) { return item.getColorO; }
}
class Dimension { public int x, y. z; }
// Не работает -- сначала класс, потом интерфейсы: // class ColoredDimensiол<Т extends HasColor & Dimension> {
// Несколько ограничений-
class ColoredDimension