русс | укр

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

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

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

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


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

Проблемы на границах


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


Пожалуй, самый странный аспект параметризации, обусловленный стиранием, заключается в возможности представления заведомо бессмысленных вещей. Пример:

//: generics/ArrayMaker.java

import java.lang.reflect.*;

import java.util.*;

 

public class ArrayMaker<T> {

private Class<T> kind;

public ArrayMaker(Class<T> kind) { this.kind = kind; }

@SuppressWarnings("unchecked")

T[] create(int size) {

return (T[])Array.newInstance(kind, size);

}

public static void main(String[] args) {

ArrayMaker<String> stringMaker =

new ArrayMaker<String>(String.class);

String[] stringArray = stringMaker.create(9);

System.out.println(Arrays.toString(stringArray));

}

}

<spoiler text="Output:">

[null, null, null. null, null, null. null. null, null]

</spoiler> Несмотря на то что объект kind хранится в виде Class<T>, стирание означает, что фактически он хранится в виде Class без параметра. Следовательно, при выполнении с ним каких-либо операций (например, при создании массива) Array.newInstance() не обладает информацией о типе, подразумеваемой kind. Метод не сможет выдать нужный результат, не требующий преобразования типа, а это приводит к выдаче предупреждения, с которым вам не удастся справиться. Обратите внимание: для создания массивов в параметризованном коде рекомендуется использовать Array.newInstance(). Если вместо массива создается другой контейнер, ситуация меняется:

//: generics/ListMaker.java

import java.util.*;

 

public class ListMaker<T> {

List<T> create() { return new ArrayList<T>(); }

public static void main(String[] args) {

ListMaker<String> stringMaker= new ListMaker<String>();

List<String> stringList = stringMaker.create();



}

}

Компилятор не выдает предупреждений, хотя мы знаем, что <Т> в new ArrayList<T>() внутри create() удаляется — во время выполнения <Т> внутри класса нет, поэтому здесь его присутствие выглядит бессмысленным. Однако если вы попробуете применить эту идею на практике и преобразуете выражение в new ArrayList(), компилятор выдаст предупреждение. Но действительно ли этот элемент не имеет смысла? Что произойдет, если мы поместим в список несколько объектов, прежде чем возватим его?

//: generics/FilledListMaker.java

import java.util.*;

 

public class FilledListMaker<T> {

List<T> create(T t, int n) {

List<T> result = new ArrayList<T>();

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

result.add(t);

return result;

}

public static void main(String[] args) {

FilledListMaker<String> stringMaker =

new FilledListMaker<String>();

List<String> list = stringMaker.create("Hello", 4);

System.out.println(list);

}

}

<spoiler text="Output:">

[Hello, Hello. Hello. Hello]

</spoiler> Хотя компилятор ничего не может знать о Т в create(), он все равно способен проверить — на стадии компиляции — что заносимые в result объекты имеют тип Т и согласуются с ArrayList<T>. Таким образом, несмотря на то что стирание удаляет информацию о фактическом типе внутри метода или класса, компилятор все равно может проверить корректность использования типа в методе или классе. Так как стирание удаляет информацию о типе внутри тела метода, на стадии выполнения особую роль приобретают границы — точки, в которых объект входит и выходит из метода. Именно в этих точках компилятор выполняет проверку типов и вставляет код преобразования. Рассмотрим следующий параметризованный пример:

//: generics/SimpleHolder.java

public class SimpleHolder {

private Object obj;

public void set(Object obj) { this.obj = obj; }

public Object get() { return obj; }

public static void main(String[] args) {

SimpleHolder holder = new SimpleHolder();

holder.set("Item");

String s = (String)holder.get();

}

}

Декомпилировав результат командой javap -с SimpleHolder, мы получим (после редактирования): <spoiler text="Byte-code:">

public void set(java lang Object);

 

0: aload_0

1: aload 1

2: putfield #2; II Поле obj.Object;

5: return public java lang.Object get().

0: aload 0

1: getfield #2; II Поле obj-Object,

4: areturn public static void main(java lang.StringE]);

0: new #3, // Класс SimpleHolder

3: dup

4: invokespecial #4; // Метод "<init>".()V

7: astore_l

8: aload 1

9: ldc #5; II String Item

11: invokevirtual #6; // Метод set (Object;)V

14: aload_l

15: invokevirtual #7, // Метод get:()Object:

18: checkcast #8, //'Класс java/lang/String

21: astore_2

22: return

</spoiler> Методы set() и get() просто записывают и читают значение, а преобразование проверяется в точке вызова get(). Теперь включим параметризацию в приведенный фрагмент:

//: generics/GenericHolder.java

public class GenericHolder<T> {

private T obj;

public void set(T obj) { this.obj = obj; }

public T get() { return obj; }

public static void main(String[] args) {

GenericHolder<String> holder =

new GenericHolder<String>();

holder.set("Item");

String s = holder.get();

}

}

Необходимость преобразования выходного значения get() отпала, но мы также знаем, что тип значения, передаваемого set(), проверяется во время компиляции. Соответствующий байт-код: <spoiler text="Byte-code:">

public void set(java.lang.Object);

 

0:aload_0

1:aload_l

2:putfield #2: // Поле obj:0bject:

5:return public java.lang.Object get():

0:aload_0

1:getfield #2; // Поле obj:0bject:

4:areturn public static void main(java.lang.String[]);

0.new #3: // Класс GenericHolder

3:dup

4:invokespecial #4; // Метод "<init>"-()V

7:astore_l

8:aload_l

9:ldc #5; // String Item

11:invokevirtual #6; II Метод set:(Object:)V

14:aload_l

15:invokevirtual #7; // Метод get:()Object:

18:checkcast #8: // Класс java/lang/String

21:astore_2

22:return

</spoiler> Как видите, байт-код идентичен. Дополнительная работа по проверке входного типа set() выполняется компилятором «бесплатно». Преобразование выходного значения get() по-прежнему сохранилось, но, по крайней мере, вам не приходится выполнять его самостоятельно — оно автоматически вставляется компилятором.



<== предыдущая лекция | следующая лекция ==>
Проблемы стирания | Компенсация за стирание


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


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

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

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


 


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

 
 

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

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