русс | укр

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

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

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

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


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

Неограниченные метасимволы


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


Казалось бы, неограниченный метасимвол <?> должен означать «все, что угодно», а его использование эквивалентно использованию низкоуровневого типа. В самом деле, на первый взгляд компилятор подтверждает эту оценку:

//: generics/UnboundedWildcards1.java

import java.util.*;

 

public class UnboundedWildcards1 {

static List list1;

static List<?> list2;

static List<? extends Object> list3;

static void assign1(List list) {

list1 = list;

list2 = list;

// list3 = list;// Предупреждение: непроверенное преобразование

// Обнаружен List, требуется List<? extends Object>

}

static void assign2(List<?> list) {

list1 = list;

list2 = list;

list3 = list;

}

static void assign3(List<? extends Object> list) {

list1 = list;

list2 = list;

list3 = list;

}

public static void main(String[] args) {

assign1(new ArrayList());

assign2(new ArrayList());

// assign3(new ArrayList());// Предупреждение-

// Непроверенное преобразование. Обнаружен- ArrayList

// Требуется: List<? extends Object>

assign1(new ArrayList<String>());

assign2(new ArrayList<String>());

assign3(new ArrayList<String>());

// Приемлемы обе формы- List<?>

List<?> wildList = new ArrayList();

wildList = new ArrayList<String>();

assign1(wildList);

assign2(wildList);

assign3(wildList);

}

}

Во многих ситуациях, подобных рассмотренной, для компилятора совершенно не существенно, используется низкоуровневый тип или <?>. Конструкцию <?> можно считать обычным украшением; впрочем, она обладает некоторой практи­ческой ценностью, потому что фактически означает: «Код написан с учетом параметризации Java, и здесь эта конструкция означает не то, что я использую низкоуровневый тип, а то, что параметр параметризации может содержать про­извольный тип». Второй пример демонстрирует важное практическое использование неограниченных метасимволов. Когда вы имеете дело с несколькими параметрами, иногда важно указать, что один параметр может относиться к произвольному типу, а другой ограничить определенным типом:



//: generics/UnboundedWildcards2.java

import java.util.*;

 

public class UnboundedWildcards2 {

static Map map1;

static Map<?,?> map2;

static Map<String,?> map3;

static void assign1(Map map) { map1 = map; }

static void assign2(Map<?,?> map) { map2 = map; }

static void assign3(Map<String,?> map) { map3 = map; }

public static void main(String[] args) {

assign1(new HashMap());

assign2(new HashMap());

// assign3(new HashMap()); // Предупреждение:

// Непроверенное преобразование. Обнаружен: HashMap

// Требуется: Map<String,?>

assign1(new HashMap<String,Integer>());

assign2(new HashMap<String,Integer>());

assign3(new HashMap<String,Integer>());

}

}

Когда в записи используются только неограниченные метасимволы, как в примере Мар<?,?>, компилятор не отличает такой тип от Map. Кроме того, пример UnboundedWildcardsl.java показывает, что компилятор по-разному интерпретирует List<?> и List<? extends Object>. Ситуация осложняется тем, что компилятор не всегда интересуется различиями между List и List<?> (например), поэтому может показаться, что это одно и то же. В самом деле, поскольку параметризованный аргумент стирается до первого ограничения, List<?> кажется эквивалентным List<Object>, a List, по сути, тоже является List<Object> — однако ни одно из этих утверждений не является в полной мере истинным. List в действительности означает «низкоуровневый List, содержащий любой тип Object», тогда как List<?> означает «не-низкоуровневый List, содержащий какой-то конкретный тип, хотя мы не знаем, какой именно». Когда же компилятор различает низкоуровневые типы и типы с неограниченными метасимволами? В следующем примере используется класс Holder<T>, определение которого приводилось ранее. Класс содержит методы, получающие аргумент Holder, но в разных формах: в виде низкоуровневого типа, с конкретным параметром типа, с неограниченным метасимволом:

//: generics/Wildcards.java

// Exploring the meaning of wildcards.

// Исследование значения метасимволов

 

public class Wildcards {

// Низкоуровневый аргумент:

static void rawArgs(Holder holder, Object arg) {

// holder.set(arg); // Предупреждение

// Непроверенный вызов set(T) как члена

// низкоуровневого типа Holder

// holder.set(new Wildcards());// To же предупреждение

// Невозможно: нет информации о 'Т'

// T t = holder.get();

 

// Допустимо, но информация типа теряется

Object obj = holder.get();

}

// По аналогии с rawArgs(), но ошибки вместо предупреждений:

static void unboundedArg(Holder<?> holder, Object arg) {

// holder.set(arg); // Ошибка:

// set(capture of ?) в Holder<capture of ?>

// не может применяться к (Object)

// holder.set(new Wildcards()); // Та же ошибка

 

// Невозможно; нет информации о 'T':

// T t = holder.get();

 

// Допустимо, но информация типа теряется:

Object obj = holder.get();

}

static <T> T exact1(Holder<T> holder) {

T t = holder.get();

return t;

}

static <T> T exact2(Holder<T> holder, T arg) {

holder.set(arg);

T t = holder.get();

return t;

}

static <T>

T wildSubtype(Holder<? extends T> holder, T arg) {

// holder.set(arg); // Ошибка:

// set(capture of ? extends T) in

// Holder<capture of ? extends T>

// cannot be applied to (T)

T t = holder.get();

return t;

}

static <T>

void wildSupertype(Holder<? super T> holder, T arg) {

holder.set(arg);

// T t = holder.get(); // Ошибка:

// Несовместимые типы: обнаружен Object, требуется T

 

// Допустимо, но информация типа теряется:

Object obj = holder.get();

}

public static void main(String[] args) {

Holder raw = new Holder<Long>();

// Или:

raw = new Holder();

Holder<Long> qualified = new Holder<Long>();

Holder<?> unbounded = new Holder<Long>();

Holder<? extends Long> bounded = new Holder<Long>();

Long lng = 1L;

 

rawArgs(raw, lng);

rawArgs(qualified, lng);

rawArgs(unbounded, lng);

rawArgs(bounded, lng);

 

unboundedArg(raw, lng);

unboundedArg(qualified, lng);

unboundedArg(unbounded, lng);

unboundedArg(bounded, lng);

 

// Object r1 = exact1(raw);// Предупреждение

// Непроверенное преобразование Holder в Holder<T>

// Непроверенный вызов метода: exactl(Holder<T>)

// применяется к (Holder)

Long r2 = exact1(qualified);

Object r3 = exact1(unbounded); // Должен возвращать Object

Long r4 = exact1(bounded);

 

// Long r5 = exact2(raw, lng); // Предупреждения-

// Непроверенное преобразование Holder в Holder<Long>

// Непроверенный вызов метода. exact2(Holder<T>,T)

// применяется к (Holder,Long)

Long r6 = exact2(qualified, lng);

// Long r7 = exact2(unbounded, lng);// Ошибка:

// exact2(Holder<T>.T) не может применяться к

// (Holder<capture of ?>.Long)

// Long r8 = exact2(bounded, lng); // Ошибка:

// exact2(Holder<T>.T) не может применяться

// к (Holder<capture of ? extends Long>,Long)

// Long r9 = wildSubtype(raw, lng); // Предупреждения

// Непроверенное преобразование Holder

// к Holder<? extends Long>

// Непроверенный вызов метода-

// wildSubtype(Holder<? extends T>,T)

// применяется к (Holder.Long)

Long r10 = wildSubtype(qualified, lng);

// Допустимо, но возвращать может только Object-

Object r11 = wildSubtype(unbounded, lng);

Long r12 = wildSubtype(bounded, lng);

// wildSupertype(raw, lng);// Предупреждения.

// Непроверенное преобразование Holder

// к Holder<? super Long>

// Непроверенный вызов метода:

// wildSupertype(Holder<? super T>,T)

// применяется к (Holder.Long)

wildSupertype(qualified, lng);

// wildSupertype(unbounded, lng); // Ошибка:

// wi1dSupertype(Hoider<? super T>,T) не может

// применяться к (Holder<capture of ?>,Long)

// wildSupertype(bounded, lng); // Ошибка:

// wildSupertype(Holder<? super T>,T) не может

// применяться к (Holder<capture of ? extends Long>.Long)

}

}

В методе rawArgs() компилятор знает, что Holder является параметризованным типом, поэтому несмотря на то, что здесь он выражен как низкоуровневый тип, компилятору известно, что передача Object методу set() небезопасна. Так как в данном случае используется низкоуровневый тип, методу set() можно передать объект произвольного типа, и он будет преобразован в Object. Таким образом, при использовании низкоуровневого типа вы лишаетесь проверки на стадии компиляции. Вызов get() демонстрирует ту же проблему: никакого Тнет, поэтому результатом может быть только Object. Может создаться впечатление, что низкоуровневый Holder и Holder<?> — приблизительно одно и то же. Однако метод unboundedArgs() демонстрирует различия между ними — в нем выявляются те же проблемы, но информация о них выдается в виде ошибок, а не предупреждений, поскольку низкоуровневый Holder может содержать разнородные комбинации типов, тогда как Holder<?> содержит однородную коллекцию одного конкретного типа. В exact1() и exact2() используются точные параметры типов (то есть без метасимволов). Мы видим, что exact2() обладает иными ограничениями, нежели exact1(), из-за дополнительного аргумента. В wildSubtype() ограничения на тип Holder опускаются до Holder с элементами любого типа, удовлетворяющими условию extends Т. И снова это означает, что Т может быть типом Fruit, a holder сможет вполне законно стать Holder <Apple>. Чтобы предотвратить возможное размещение Orange в Holder<Apple>, вызовы set() (и любых других методов, получающих в аргументах параметр типа) запрещены. Однако мы знаем, что все объекты, полученные из Holder<? extends Fruit>, по меньшей мере, являются Fruit, поэтому вызов get() (или любого метода с возвращаемым значением параметра типа) допустим.



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


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


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

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

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


 


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

 
 

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

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