Чтение онлайн

на главную - закладки

Жанры

Философия Java3

Эккель Брюс

Шрифт:

Из-за этого механизму параметризации Java приходится поддерживать не только обратную совместимость (существующий код и файлы классов остаются абсолютно законными и сохраняют свой прежний смысл), но и миграционную совместимость — чтобы библиотеки могли переводиться в параметризованную форму в собственном темпе, причем их параметризация не влияла бы на работу зависящего от него кода и приложений. Выбрав эту цель, проектировщики Java и различные группы, работавшие над проблемой, решили, что единственным приемлемым решением является стирание, позволяющее непарамет-ризованному коду нормально сосуществовать с параметризованным.

Проблемы стирания

Итак, главным аргументом для применения стирания является процесс перехода с непараметризованного кода на параметризованный и интеграция параметризации в язык без нарушения работы существующих библиотек. Стирание позволяет использовать существующий ^параметризованный код без изменений, пока клиент не будет готов переписать свой код с использованием параметризации.

Однако за стирание приходится расплачиваться. Параметризованные типы не могут использоваться в операциях, в которых явно задействованы типы времени выполнения — преобразования типов, instanceof и выражения new. Вся информация о типах параметров теряется, и при написании параметризованного кода вам придется постоянно напоминать себе об этом. Допустим, вы пишете фрагмент кода

class Foo<T> { Т var;

}

Может показаться, что при создании экземпляра Foo:

Foo<Cat> f = new Foo<Cat>,

код class Foo должен знать, что он работает с Cat. Синтаксис создает впечатление, что тип Т подставляется повсюду внутри класса. Но на самом деле это не так, и при написании кода для класса вы должны постоянно напоминать себе: «Нет, это всего лишь Object».

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

//: generics/ErasureAndlnheritance.java

class GenericBase<T> { private T element;

public void set(T arg) { arg = element; } public T get О { return element. }

}

class Derivedl<T> extends GenericBase<T> {}

class Derived2 extends GenericBase {} // Без предупреждений

// class Derived3 extends GenericBase<?> {}

// Странная ошибка.

// Обнаружен непредвиденный тип : ?

// требуется- класс или интерфейс без ограничений

public class ErasureAndlnheritance { @SuppressWarni ngs("unchecked") public static void main(String[] args) { Derived2 d2 = new Derived2; Object obj = d2.get; d2.set(obj); // Предупреждение!

}

} ///

Derived2 наследует от GenericBase без параметризации, и компилятор не выдает при этом никаких предупреждений. Предупреждение выводится позже, при вызове set.

Для подавления этого предупреждения в Java существует директива, приведенная в листинге (до выхода Java SE5 она не поддерживалась):

@SuppressWarnings("unchecked")

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

Ошибка, выдаваемая в Derived3, означает, что компилятор рассчитывает увидеть «обычный» базовый класс.

Добавьте к этому дополнительные усилия на управление ограничениями, если вы не желаете интерпретировать параметр типа как простой Object, — и что мы получаем в остатке? Гораздо больше хлопот при гораздо меньше, пользе по сравнению с параметризованными типами в языках вроде С++, Ada или Eiffel. Конечно, это вовсе не означает, что эти языки в целом эффективнее Java в большинстве задач программирования, а говорит лишь о том, что их механизмы параметризации типов отличаются большей гибкостью и мощью, чем в Java.

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

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

//: 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; } @SuppressWarni ngs("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.pri ntin(Arrays.toStri ng(stri ngArray));

}

} /* Output;

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

*///:-

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

Обратите внимание: для создания массивов в параметризованном коде рекомендуется использовать Array.newlnstance.

Если вместо массива создается другой контейнер, ситуация меняется:

//: 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.createO;

}

} ///:-

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

Но действительно ли этот элемент не имеет смысла? Что произойдет, если мы поместим в список несколько объектов, прежде чем возватим его?

Поделиться:
Популярные книги

Черная метка

Лисина Александра
7. Гибрид
Фантастика:
технофэнтези
аниме
фэнтези
попаданцы
5.00
рейтинг книги
Черная метка

Мечников. Битва умов

Алмазов Игорь
10. Жизнь Лекаря с нуля
Фантастика:
альтернативная история
аниме
фэнтези
попаданцы
5.00
рейтинг книги
Мечников. Битва умов

Стеллар. Трибут

Прокофьев Роман Юрьевич
2. Стеллар
Фантастика:
боевая фантастика
рпг
8.75
рейтинг книги
Стеллар. Трибут

Лекарь Империи 5

Карелин Сергей Витальевич
5. Лекарь Империи
Фантастика:
городское фэнтези
аниме
героическая фантастика
попаданцы
5.00
рейтинг книги
Лекарь Империи 5

Звездная Кровь. Изгой VII

Елисеев Алексей Станиславович
7. Звездная Кровь. Изгой
Фантастика:
боевая фантастика
технофэнтези
рпг
фантастика: прочее
попаданцы
5.00
рейтинг книги
Звездная Кровь. Изгой VII

Последний Герой. Том 5

Дамиров Рафаэль
5. Последний герой
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Последний Герой. Том 5

Я еще не барон

Дрейк Сириус
1. Дорогой барон!
Фантастика:
боевая фантастика
попаданцы
аниме
5.00
рейтинг книги
Я еще не барон

Камень. Книга пятая

Минин Станислав
5. Камень
Фантастика:
боевая фантастика
6.43
рейтинг книги
Камень. Книга пятая

Офицер

Земляной Андрей Борисович
1. Офицер
Фантастика:
боевая фантастика
7.21
рейтинг книги
Офицер

Гримуар темного лорда IV

Грехов Тимофей
4. Гримуар темного лорда
Фантастика:
фэнтези
боевая фантастика
попаданцы
аниме
5.00
рейтинг книги
Гримуар темного лорда IV

Локки 8. Потомок бога

Решетов Евгений Валерьевич
8. Локки
Фантастика:
фэнтези
боевая фантастика
героическая фантастика
попаданцы
5.00
рейтинг книги
Локки 8. Потомок бога

Вернуть невесту. Ловушка для попаданки 2

Ардова Алиса
2. Вернуть невесту
Любовные романы:
любовно-фантастические романы
7.88
рейтинг книги
Вернуть невесту. Ловушка для попаданки 2

Жена неверного генерала, или Попаданка на отборе

Удалова Юлия
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
Жена неверного генерала, или Попаданка на отборе

На границе империй. Том 8

INDIGO
12. Фортуна дама переменчивая
Фантастика:
космическая фантастика
попаданцы
5.00
рейтинг книги
На границе империй. Том 8