Spec-Zone .ru
спецификации, руководства, описания, API
|
Чтобы использовать обобщения Java эффективно, следует рассмотреть следующие ограничения:
Рассмотрите следующий параметризованный тип:
class Pair<K, V> { private K key; private V value; public Pair(K key, V value) { this.key = key; this.value = value; } // ... }
Создавая объект Pair, Вы не можете subsitute тип примитива для параметра типа K или V:
Pair<int, char> p = new Pair<>(8, 'a'); // compile-time error
Можно заменить только нетипами примитивов параметры типа K и V:
Pair<Integer, Character> p = new Pair<>(8, 'a');
Отметьте что автополя 8 to Integer.valueOf(8) и 'a' компилятора Java к Character('a'):
Pair<Integer, Character> p = new Pair<>(Integer.valueOf(8), new Character('a'));
Для получения дополнительной информации по автоупаковке см. Автоупаковку и Распаковывание в уроке Чисел и Строк.
Невозможно создать экземпляр параметра типа. Например, следующий код вызывает ошибку времени компиляции:
public static <E> void append(List<E> list) { E elem = new E(); // compile-time error list.add(elem); }
Как обходное решение, можно создать объект параметра типа посредством отражения:
public static <E> void append(List<E> list, Class<E> cls) throws Exception { E elem = cls.newInstance(); // OK list.add(elem); }
Можно вызвать метод append следующим образом:
List<String> ls = new ArrayList<>(); append(ls, String.class);
Статическим полем class является class уровень переменная, совместно использованная всеми нестатическими объектами class. Следовательно, статические поля параметров типа не позволяются. Рассмотрите следующий class:
public class MobileDevice<T> { private static T os; // ... }
Если бы статические поля параметров типа были позволены, то следующий код был бы перепутан:
MobileDevice<Smartphone> phone = new MobileDevice<>(); MobileDevice<Pager> pager = new MobileDevice<>(); MobileDevice<TabletPC> pc = new MobileDevice<>();
Поскольку статическое поле os совместно используется phone, pager, и pc, каков фактический тип os? Это не может быть Smartphone, Pager, и TabletPC одновременно. Невозможно, поэтому, создать статические поля параметров типа.
Поскольку компилятор Java стирает все параметры типа в универсальном коде, невозможно проверить, который параметризовал тип для универсального типа, используется во времени выполнения:
public static <E> void rtti(List<E> list) { if (list instanceof ArrayList<Integer>) { // compile-time error // ... } }
Набор параметризованных типов, которые передают к методу rtti:
S = { ArrayList<Integer>, ArrayList<String> LinkedList<Character>, ... }
Время выполнения не отслеживает параметры типа, таким образом, оно не может сказать различие между ArrayList<Integer> и ArrayList<String>. Наиболее можно сделать, должен использовать неограниченный подстановочный знак, чтобы проверить, что списком является ArrayList:
public static void rtti(List<?> list) { if (list instanceof ArrayList<?>) { // OK; instanceof requires a reifiable type // ... } }
Как правило, невозможно бросить к параметризованному типу, если он не параметризован неограниченными подстановочными знаками. Например:
List<Integer> li = new ArrayList<>(); List<Number> ln = (List<Number>) li; // compile-time error
Однако, в некоторых случаях компилятор знает, что параметр типа всегда допустим и позволяет бросок. Например:
List<String> l1 = ...; ArrayList<String> l2 = (ArrayList<String>)l1; // OK
Невозможно создать массивы параметризованных типов. Например, следующий код не компилирует:
List<Integer>[] arrayOfLists = new List<Integer>[2]; // compile-time error
Следующий код иллюстрирует то, что происходит, когда различные типы вставляются в массив:
Object[] strings = new String[2]; strings[0] = "hi"; // OK strings[1] = 100; // An ArrayStoreException is thrown.
Если бы Вы пробуете ту же самую вещь универсальным списком, была бы проблема:
Object[] stringLists = new List<String>[]; // compiler error, but pretend it's allowed stringLists[0] = new ArrayList<String>(); // OK stringLists[1] = new ArrayList<Integer>(); // An ArrayStoreException should be thrown, // but the runtime can't detect it.
Если бы массивам параметризованных списков разрешили, то предыдущий код был бы не в состоянии бросить требуемый ArrayStoreException.
Универсальный class не может расширить Throwable class прямо или косвенно. Например, следующие классы не будут компилировать:
// Extends Throwable indirectly class MathException<T> extends Exception { /* ... */ } // compile-time error // Extends Throwable directly class QueueFullException<T> extends Throwable { /* ... */ // compile-time error
Метод не может поймать экземпляр параметра типа:
public static <T extends Exception, J> void execute(List<J> jobs) { try { for (J job : jobs) // ... } catch (T e) { // compile-time error // ... } }
Можно, однако, использовать параметр типа в пункте throws:
class Parser<T extends Exception> { public void parse(File file) throws T { // OK // ... } }
У class не может быть двух перегруженных методов, у которых будет та же самая подпись после стирания типа.
public class Example { public void print(Set<String> strSet) { } public void print(Set<Integer> intSet) { } }
Перегрузки все совместно использовали бы то же самое classfile представление и генерируют ошибку времени компиляции.