Содержание
· 1 ПАРАМЕТРИЗАЦИЯ
o 1.1 Простая параметризация
o 1.2 Кортежи
o 1.3 Класс стека
o 1.4 RandomList
o 1.5 Параметризованные интерфейсы
o 1.6 Параметризованные методы
o 1.7 Вычисление типа аргумента
o 1.8 Явное указание типа
o 1.9 Использование параметризованных методов с Generator
o 1.10 Обобщенный генератор
o 1.11 Упрощение работы с кортежами
o 1.12 Вспомогательный класс Set
o 1.13 Анонимные внутренние классы
o 1.14 Построение сложных моделей
o 1.15 Тайна стирания
o 1.16 Подход C++
o 1.17 Миграционная совместимость
o 1.18 Проблемы стирания
o 1.19 Проблемы на границах
o 1.20 Компенсация за стирание
o 1.21 Создание экземпляров типов
o 1.22 Массивы параметризованных типов
o 1.23 Ограничения
o 1.24 Метасимволы
o 1.25 Насколько умен компилятор?
o 1.26 Контравариантность
o 1.27 Неограниченные метасимволы
o 1.28 Реализация параметризованных интерфейсов
o 1.29 Преобразования типов и предупреждения
o 1.30 Перегрузка
o 1.31 Резюме
Обычные классы и методы работают с конкретными типами: либо, примитивами, либо с классами. Если ваш код должен работать с разными типами, такая жесткость может создавать проблемы.
Одним из механизмов обеспечения универсальности кода в объектно-ориентированных языках является полиморфизм. Например, вы можете написать метод, который получает в аргументе объект базового класса, а затем использует этот метод с любым классом, производным от него. Метод становится чуть более универсальным, а область его применения расширяется. Это относится и к классам — использование базового класса вместо производного обеспечивает дополнительную гибкость. Конечно, наследование возможно только для классов, не являющихся final.
Впрочем, иногда даже рамки одной иерархии оказываются слишком тесными. Если в аргументе метода передается интерфейс вместо класса, то ограничения ослабляются и в них включается все, что реализует данный интерфейс, — в том числе и классы, которые еще не были созданы. Это дает программисту-клиенту возможность реализовать интерфейс, чтобы соответствовать требованиям вашего класса или метода. Таким образом, интерфейсы позволяют выходить за рамки иерархий классов, если только у вас имеется возможность создать новый класс. Но в некоторых случаях даже интерфейсы оказываются недостаточно гибкими. Интерфейс требует, чтобы ваш код работал в этом конкретном интерфейсе. Если бы было можно указать, что ваш код работает «с некоторым не заданным типом», а не с конкретным интерфейсом или классом, программа приобрела бы еще более общий характер.
В этом и состоит концепция параметризации — одного из самых значительных новшеств Java SE5. Параметризованные типы позволяют создавать компоненты (прежде всего, контейнеры), которые могут легко использоваться с разными типами. Если прежде вы еще никогда не встречались с механизмом параметризации в действии, вероятно, параметризованные типы Java покажутся вам довольно удобным дополнением к языку. При создании экземпляра параметризованного типа преобразования типа выполняются автоматически, а правильность типов проверяется на стадии компиляции. С другой стороны, разработчики с опытом использования параметризованных типов в других языках (скажем, в C++) увидят, что в Java они не соответствуют всем ожиданиям.
Если использовать готовый параметризованный тип относительно несложно, при попытке написать собственный тип вас ждут сюрпризы. В частности, в этой главе я постараюсь объяснить, почему параметризованные типы Java получились именно такими. Не стоит думать, что параметризованные типы Java бесполезны — во многих случаях они делают код более четким и элегантным. Но, если вы работали на другом языке, в котором они были реализованы более «чисто», вас могут ждать разочарования. В этой главе мы изучим как достоинства, так и недостатки параметризованных типов Java, чтобы вы могли использовать эту новую возможность более эффективно.