русс | укр

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

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

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

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


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

Насколько умен компилятор?


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


Казалось бы, из всего сказанного следует, что вызов любых методов с аргументами невозможен, но рассмотрим следующий пример:

//: generics/CompilerIntelligence.java

import java.util.*;

 

public class CompilerIntelligence {

public static void main(String[] args) {

List<? extends Fruit> flist =

Arrays.asList(new Apple());

Apple a = (Apple)flist.get(0); // No warning

flist.contains(new Apple()); // Argument is 'Object'

flist.indexOf(new Apple()); // Argument is 'Object'

}

}

Как видите, вызовы contains() и indexOf() с аргументами Apple воспринимаются нормально. Означает ли это, что компилятор действительно анализирует код, чтобы узнать, модифицирует ли некоторый метод свой объект? Просмотр документации ArrayList показывает, что компилятор не настолько умен. Если add() получает аргумент параметризующего типа, contains() и indexOf() получают аргумент типа Object. Таким образом, когда вы указываете ArrayList<? extends Fruit>, аргумент add() превращается в <? extends Fruit>. По этому описанию компилятор не может определить, какой именно подтип Fruit требуется в данном случае, поэтому не принимает никакие типы Fruit. Даже если вы предварительно преобразуете Apple в Fruit, компилятор все равно откажется вызывать метод (например, add()), если в списке аргументов присутствует метасимвол. У методов contains() и indexOf() аргументы относятся к типу Object, метасимволы в них отсутствуют, поэтому компилятор разрешает вызов. Это означает, что проектировщик параметризованного класса должен сам решить, какие вызовы «безопасны», и использовать типы Object для их аргументов. Чтобы сделать невозможным вызов при использовании типа с метасимволами, включите параметр типа в список аргументов. В качестве примера рассмотрим очень простой класс Holder:



//: 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();

}

}

//---------------------------------------

//: generics/Holder.java

public class Holder<T> {

private T value;

public Holder() {}

public Holder(T val) { value = val; }

public void set(T val) { value = val; }

public T get() { return value; }

public boolean equals(Object obj) {

return value.equals(obj);

}

public static void main(String[] args) {

Holder<Apple> Apple = new Holder<Apple>(new Apple());

Apple d = Apple.get();

Apple.set(d);

// Holder<Fruit> Fruit = Apple; // Повышение невозможно

Holder<? extends Fruit> fruit = Apple; // OK

Fruit p = fruit.get();

d = (Apple)fruit.get(); // Возвращает 'Object'

try {

Orange c = (Orange)fruit.get(); // Предупреждения нет

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

// fruit.set(new Apple()); // Вызов set() невозможен

// fruit.set(new Fruit()); // Вызов set() невозможен

System.out.println(fruit.equals(d)); // OK

}

}

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

java.lang.ClassCastException. Apple cannot be cast to Orange

true

</spoiler> Holder содержит метод set(), получающий T; метод get(), возвращающий Т; и метод equals(), получающий Object. Как вы уже видели, Holder<Apple> невозможно преобразовать в Holder<Fruit>, но зато можно в Holder<? extends Fruit>. При вызове get() будет возвращен только тип Fruit — то, что известно компилятору по ограничению «все, что расширяет Fruit». Если вы располагаете дополнительной информацией, то сможете выполнить преобразование к конкретному типу Fruit и обойтись без предупреждений, но с риском исключения ClassCastException. Метод set() не работает ни с Apple, ни с Fruit, потому что аргумент set() тоже содержит «? extends Fruit»; по сути, он может быть чем угодно, а компилятор не может проверить безопасность типов для «чего угодно». Впрочем, метод equals()работает нормально, потому что он получает Object вместо Т. Таким образом, компилятор обращает внимание только на типы передаваемых и возвращаемых объектов. Он не анализирует код, проверяя, выпол­няются ли реальные операции чтения или записи.

Контравариантность

Также можно пойти другим путем и использовать метасимволы супертипов. В этом случае вы сообщаете, что метасимвол ограничивается базовым классом некоторого класса; при этом используется запись <? super MyClass>, и даже с па­раметром типа <? super Т>. Это позволяет безопасно передавать типизованный объект параметризованному типу. Таким образом, с использованием метасимволов супертипов становится возможной запись в коллекцию:

//: generics/SuperTypeWildcards.java

import java.util.*;

 

public class SuperTypeWildcards {

static void writeTo(List<? super Apple> apples) {

apples.add(new Apple());

apples.add(new Jonathan());

// apples.add(new Fruit()); // Error

}

}

Аргумент apples является контейнером List для некоторого типа, являющегося базовым для Apple; из этого следует, что Apple и производные от Apple типы могут безопасно включаться в контейнер. Но, поскольку нижним ограничением является Apple, мы не знаем, безопасно ли включать Fruit в такой List, так как это откроет List для добавления типов, отличных от Apple, с нарушением статической безопасности типов. Ограничения супертипов расширяют возможности по передаче аргументов методу:

//: generics/GenericWriting.java

import java.util.*;

 

public class GenericWriting {

static <T> void writeExact(List<T> list, T item) {

list.add(item);

}

static List<Apple> apples = new ArrayList<Apple>();

static List<Fruit> fruit = new ArrayList<Fruit>();

static void f1() {

writeExact(apples, new Apple());

// writeExact(fruit, new Apple()); // Ошибка:

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

}

static <T> void

writeWithWildcard(List<? super T> list, T item) {

list.add(item);

}

static void f2() {

writeWithWildcard(apples, new Apple());

writeWithWildcard(fruit, new Apple());

}

public static void main(String[] args) { f1(); f2(); }

}

Метод writeExact() использует параметр типа «как есть», без метасимволов. На примере f1() мы видим, что этот способ отлично работает — при условии, что в List<Apple> помещаются только объекты Apple. Однако writeExact() не позволяет поместить Apple в List<Fruit>, хотя мы знаем, что это должно быть возможно. В writeWithWildcard() используется аргумент List<? super Т>, поэтому List содержит конкретный тип, производный от Т; следовательно, Т или производные от него типы могут безопасно передаваться в аргументе методов List. Пример встречается в f2: как и прежде, Apple можно поместить в List<Apple>, но, как и предполагалось, также стало можно поместить Apple в List<Fruit>.



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


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


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

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

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


 


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

 
 

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

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