Читаем Философия Java3 полностью

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

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

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

//: generics/Compi1erIntel 1 igence.java

import java.util .*;

public class Compilerlntelligence {

public static void main(String[] args) { List flist =

Arrays.asList(new AppleO); Apple a = (Apple)flist.get(O); // Без предупреждений fli st. contains (new AppleO); //Аргумент 'Object' fl i st. indexOf (new AppleO); //Аргумент 'Object'

}

} ///:-

Как видите, вызовы contains() и indexOf() с аргументами Apple воспринимаются нормально. Означает ли это, что компилятор действительно анализирует код, чтобы узнать, модифицирует ли некоторый метод свой объект?

Просмотр документации ArrayList показывает, что компилятор не настолько умен. Если add() получает аргумент параметризующего типа, contains() и in-dexOf() получают аргумент типа Object. Таким образом, когда вы указываете ArrayList, аргумент add() превращается в «? extends Fruit». По этому описанию компилятор не может определить, какой именно подтип Fruit требуется в данном случае, поэтому не принимает никакие типы Fruit. Даже если вы предварительно преобразуете Apple в Fruit, компилятор все равно откажется вызывать метод (например, add()), если в списке аргументов присутствует метасимвол.

У методов contains() и indexOf() аргументы относятся к типу Object, метасимволы в них отсутствуют, поэтому компилятор разрешает вызов. Это означает, что проектировщик параметризованного класса должен сам решить, какие вызовы «безопасны», и использовать типы Object для их аргументов. Чтобы сделать невозможным вызов при использовании типа с метасимволами, включите параметр типа в список аргументов.

В качестве примера рассмотрим очень простой класс Holder:

//: generics/Holder.java

public class Holder { private T value; public HolderO {}

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 = new Holder(new AppleO); Apple d = Apple.getO; Apple.set(d);

// Holder Fruit = Apple; // Повышение невозможно Holder fruit = Apple; // OK Fruit p = fruit.getO;

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

Orange с = (Orange)fruit.getO; // Предупреждения нет } catch(Exception e) { System.out.println(e); } // fruit.set(new AppleO); // Вызов setO невозможен // fruit.set(new FruitO); // Вызов setO невозможен System.out.println(fruit.equals(d)); // OK

}

} /* Output: (Sample)

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

true

*///:-

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

Впрочем, метод equalsQ работает нормально, потому что он получает Object вместо Т. Таким образом, компилятор обращает внимание только на типы передаваемых и возвращаемых объектов. Он не анализирует код, проверяя, выполняются ли реальные операции чтения или записи.

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

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

//• generics/SuperTypeWiIdcards java

import java util.*:

Перейти на страницу:

Похожие книги

Access 2002: Самоучитель
Access 2002: Самоучитель

В книге рассматривается широкий круг вопросов, связанных с использованием программной среды Access 2002, которая является составной частью пакета Office 2002 и предназначена для создания банка данных в самых различных предметных областях.Подробно описывается методика проектирования объектов базы данных (таблицы, формы, отчеты, страницы доступа к данным, запросы, модули).Детально обсуждаются вопросы создания интегрированной базы данных в единой среде Access 2002: формирование БД с нуля, конвертирование в программную среду баз данных, созданных в ином программном окружении – Clarion, FoxPro.Особое внимание уделяется формированию разнообразных запросов к интегрированной базе данных Access 2002 с использованием языков программирования SQL, VBA и макросов.Приводятся общие сведения о возможностях языка обмена данными между различными компьютерами и приложениями (XML). Описываются возможности использования гиперссылок, связывающих базу данных с другими программными продуктами. Объясняется, как можно работать с базой данных Access 2002 без установки ее на компьютер, используя технологию ODBC (Open Data Base Connectivity). В приложениях приводятся количественные параметры Access 2002 и связанная с этой СУБД терминология.Предлагаемая книга будет полезна специалистам, занимающимся практической разработкой банков данных и приложений на их основе, а также студентам вузов, изучающим информатику.

Павел Юрьевич Дубнов

Программирование, программы, базы данных / ОС и Сети / Книги по IT