русс | укр

Языки программирования

ПаскальСиАссемблерJavaMatlabPhpHtmlJavaScriptCSSC#DelphiТурбо Пролог

Компьютерные сетиСистемное программное обеспечениеИнформационные технологииПрограммирование

Все о программировании


Linux Unix Алгоритмические языки Аналоговые и гибридные вычислительные устройства Архитектура микроконтроллеров Введение в разработку распределенных информационных систем Введение в численные методы Дискретная математика Информационное обслуживание пользователей Информация и моделирование в управлении производством Компьютерная графика Математическое и компьютерное моделирование Моделирование Нейрокомпьютеры Проектирование программ диагностики компьютерных систем и сетей Проектирование системных программ Системы счисления Теория статистики Теория оптимизации Уроки AutoCAD 3D Уроки базы данных Access Уроки Orcad Цифровые автоматы Шпаргалки по компьютеру Шпаргалки по программированию Экспертные системы Элементы теории информации

Массивы параметризованных типов


Дата добавления: 2015-06-12; просмотров: 3365; Нарушение авторских прав


Как мы видели в Erased.java, создавать массивы параметризованных типов нельзя. Везде, где возникает необходимость в создании таких массивов, следует применять ArrayList:

//: generics/ListOfGenerics.java

import java.util.*;

 

public class ListOfGenerics<T> {

private List<T> array = new ArrayList<T>();

public void add(T item) { array.add(item); }

public T get(int index) { return array.get(index); }

}

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

//: generics/ArrayOfGenericReference.java

class Generic<T> {}

 

public class ArrayOfGenericReference {

static Generic<Integer>[] gia;

}

Компилятор принимает эту запись без каких-либо предупреждений. С другой стороны, вы не сможете создать массив указанного типа (включая параметры типа), поэтому все это сбивает с толку. Поскольку все массивы обладают одинаковой структурой (размер каждого элемента и способ размещения в памяти) независимо от типа хранящихся данных, создается впечатление, что вы сможете создать массив Object и преобразовать его к нужному типу. Код откомпилируется, но работать не будет — он выдает исключение ClassCastException:

//: generics/ArrayOfGeneric.java

public class ArrayOfGeneric {

static final int SIZE = 100;

static Generic<Integer>[] gia;

@SuppressWarnings("unchecked")

public static void main(String[] args) {

// Компилируется, но приводит к ClassCastException:

//! gia = (Generic<Integer>[])new Object[SIZE];



// Тип времени выполнения является "стертым" type:

gia = (Generic<Integer>[])new Generic[SIZE];

System.out.println(gia.getClass().getSimpleName());

gia[0] = new Generic<Integer>();

//! gia[1] = new Object(); // Ошибка компиляции

// Обнаруживается несоответствие типов во время компиляции:

//! gia[2] = new Generic<Double>();

}

}

<spoiler text="Output:">

Generic[]

</spoiler> Проблема в том, что массивы отслеживают свой фактический тип, который задается в точке создания массива. Таким образом, даже несмотря на то, что gia преобразуется в Generic<Integer>[], эта информация существует только на стадии компиляции (а без директивы @SuppressWarnings вы получите предупреждение). Во время выполнения мы по-прежнему имеем дело с массивом Object, и это создает проблемы. Успешно создать массив параметризованного типа можно только одним способом — создать новый массив «стертого» типа и выполнить преобразование. Рассмотрим чуть более сложный пример. Допустим, имеется простая параметризованная «обертка» для массива:

//: generics/GenericArray.java

 

public class GenericArray<T> {

private T[] array;

@SuppressWarnings("unchecked")

public GenericArray(int sz) {

array = (T[])new Object[sz];

}

public void put(int index, T item) {

array[index] = item;

}

public T get(int index) { return array[index]; }

// Метод, предоставляющий доступ к базовому представлению:

public T[] rep() { return array; }

public static void main(String[] args) {

GenericArray<Integer> gai =

new GenericArray<Integer>(10);

// Приводит к ClassCastException:

//! Integer[] ia = gai.rep();

// А так можно

Object[] oa = gai.rep();

}

}

Как и прежде, мы не можем использовать запись Т[] array = new T[sz], поэтому мы создаем массив объектов и преобразуем его. Метод rер() возвращает Т[]; в методе main() для gai это должен быть тип Integer[], но при попытке вызова и сохранения результата по ссылке наInteger[] будет получено исключение ClassCastException — это снова происходит из-за того, что фактическим типом объекта времени выполнения является Object[]. Если мы немедленно проводим преобразование к Т[], то на стадии компиляции фактический тип массива теряется и компилятор может упустить некоторые потенциальные ошибки. Из-за этого лучше использовать в коллекции Object[], а затем добавить преобразование к Т при использовании элемента массива. Вот как это будет выглядеть в примере GenericArray.java:

//: generics/GenericArray2.java

public class GenericArray2<T> {

private Object[] array;

public GenericArray2(int sz) {

array = new Object[sz];

}

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<Integer> gai =

new GenericArray2<Integer>(10);

for(int i = 0; i < 10; i ++)

gai.put(i, i);

for(int i = 0; i < 10; i ++)

System.out.print(gai.get(i) + " ");

System.out.println();

try {

Integer[] ia = gai.rep();

} catch(Exception e) { System.out.println(e); }

}

}

<spoiler text="Output:"> (Sample)

0 12 3 4 5 6 7 8 9

java.lang.ClassCastException:

[Ljava.lang.Object; cannot be cast to [Ljava.lang.Integer;

</spoiler> На первый взгляд почти ничего не изменилось, разве что преобразование типа было перемещено. Без директив @SuppressWarnings вы по-прежнему будете получать предупреждения, но теперь во внутренней реализации используется Object[] вместо Т[]. При вызове get() объект преобразуется к Т; это правильный тип, поэтому преобразование безопасно. Но при вызове rер() снова делается попытка преобразования Object[] в Т[], которое остается неверным; в результате вы получите предупреждение во время компиляции и исключение во время выполнения. Не существует способа обойти тип базового массива, которым может быть только Object[]. У внутренней интерпретации array как Object[] вместо Т[] есть свои преимущества: например, вы с меньшей вероятностью забудете тип массива, что приведет к случайному появлению ошибок (впрочем, подавляющее большинство таких ошибок будет быстро выявлено на стадии выполнения). В новом коде следует передавать метку типа. В обновленной версии GenericArray выглядит так:

//: generics/GenericArrayWithTypeToken.java

import java.lang.reflect.*;

 

public class GenericArrayWithTypeToken<T> {

private T[] array;

@SuppressWarnings("unchecked")

public GenericArrayWithTypeToken(Class<T> type, int sz) {

array = (T[])Array.newInstance(type, sz);

}

public void put(int index, T item) {

array[index] = item;

}

public T get(int index) { return array[index]; }

// Expose the underlying representation:

public T[] rep() { return array; }

public static void main(String[] args) {

GenericArrayWithTypeToken<Integer> gai =

new GenericArrayWithTypeToken<Integer>(

Integer.class, 10);

// This now works:

Integer[] ia = gai.rep();

}

}

Метка типа Class<T> передается конструктору для восстановления информации после стирания, чтобы мы могли создать фактический тип нужного массива (предупреждения при преобразовании по-прежнему приходится подавлять @SuppressWarnings). Получив фактический тип, мы возвращаем его для получения желаемых результатов, как видно из main(). К сожалению, просмотрев исходный код стандартных библиотек Java SE5, вы увидите, что преобразования массивов Object в параметризованные типы происходят повсеместно. Например, вот как выглядит копирующий конструктор для создания ArrayList из Collection после некоторой правки и упрощения:

public ArrayList(Collection с) {

size = c.size();

elementData = (E[])new Object[size];

с.toArray(elementData):

}

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



<== предыдущая лекция | следующая лекция ==>
Создание экземпляров типов | Ограничения


Карта сайта Карта сайта укр


Уроки php mysql Программирование

Онлайн система счисления Калькулятор онлайн обычный Инженерный калькулятор онлайн Замена русских букв на английские для вебмастеров Замена русских букв на английские

Аппаратное и программное обеспечение Графика и компьютерная сфера Интегрированная геоинформационная система Интернет Компьютер Комплектующие компьютера Лекции Методы и средства измерений неэлектрических величин Обслуживание компьютерных и периферийных устройств Операционные системы Параллельное программирование Проектирование электронных средств Периферийные устройства Полезные ресурсы для программистов Программы для программистов Статьи для программистов Cтруктура и организация данных


 


Не нашли то, что искали? Google вам в помощь!

 
 

© life-prog.ru При использовании материалов прямая ссылка на сайт обязательна.

Генерация страницы за: 0.008 сек.