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

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

Жанры

Философия Java3

Эккель Брюс

Шрифт:

Перегрузка

Следующий пример не компилируется, хотя на первый взгляд выглядит вполне разумно:

// generics/UseList java

// {CompileTimeError} (He компилируется)

import java.util.*;

public class UseList<W.T> { void f(List<T> v) {}

void f(List<W> v) {}

} III ~

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

II. generics/UseList2 java

import java util.*;

public class UseList2<W.T> { void fl(List<T> v) {} void f2(List<W> v) {}

} III.-

К

счастью, проблемы такого рода обнаруживаются компилятором.

Резюме

Мне довелось работать с шаблонами С++ с момента их появления. Скорее всего, приведенный далее аргумент я выдвигал в спорах чаще, чем большинство моих единомышленников. Лишь недавно я задумался над тем, насколько в действительности справедлив этот аргумент, — сколько раз проблема, которую я сейчас опишу, проникала в рабочий код?

Аргумент такой: одним из самых логичных мест для использования механизма параметризации являются контейнерные классы: List, Set, Map и т. д. До выхода Java SE5 объект, помещаемый в контейнер, преобразовывался в Object, и информация типа терялась. Если же вы хотели снова извлечь объект из контейнера, его приходилось преобразовывать к нужному типу. Я пояснял происходящее на примере List с элементами Cat (разновидность этого примера с Apple и Orange приведена в начале главы 11). Без параметризованной версии контейнера из Java SE5 вы помещаете и извлекаете из контейнера Object, поэтому в List с элементами Cat легко поместить объект Dog.

Однако версии Java, существовавшие до появления параметризации, не допускали злоупотреблений объектами, помещаемыми в контейнер. Если вы помещали Dog в контейнер Cat, а затем пытались интерпретировать все элементы контейнера как Cat, то при извлечении ссылки на Dog и ее преобразовании к Cat происходило преобразование RuntimeException. Проблема обнаруживалась, пусть и на стадии выполнения, а не во время компиляции.

В предыдущих изданиях книги я писал:

«Это не просто мелкая неприятность, а потенциальный источник трудноуловимых ошибок. Если одна часть (или несколько частей) программы вставляет объекты в контейнер, а в другой части программы обнаруживается, что в контейнер был йомещен недопустимый объект, вам придется искать, где именно была выполнена неверная операция вставки».

Но позже я задумался над этим аргументом, и у меня появились сомнения. Во-первых, насколько часто это происходит? Не помню, чтобы такая ошибка встретилась в моей программе. Когда я спрашивал людей на конференциях, мне тоже не удалось найти никого, с кем бы это случилось. В другой книге использовался пример списка с именем files, содержащего объекты String, — в этом примере казалось абсолютно логичным добавить в список объект типа File, так что объекту, вероятно, стоило присвоить имя fileNames. Какую бы проверку типов ни обеспечивал язык Java, программист все равно может написать малопонятную программу — а плохо написанная программа, даже если она компилируется, все равно остается плохо написанной. Вероятно, нормальный разработчик присвоит контейнеру понятное имя вроде cats, которое послужит предупреждением для программиста, пытающегося занести в контейнер другой объект, отличный от Cat. Но, даже если это и произойдет, как долго такая ошибка останется скрытой? Здравый смысл подсказывает, что исключение произойдет вскоре после начала тестирования с реальными данными.

Один автор даже предположил, что такая ошибка может «оставаться скрытой несколько лет». Но я что-то не помню потока сообщений от людей, у которых возникали проблемы с поиском ошибок «Dog в списке Cat», или хотя бы с их частым появлением. Так неужели такая заметная и довольно сложная возможность, как параметризация, была включена

в Java из-за проблем такого рода?

Я считаю, что побудительной причиной для включения параметризации в язык (не обязательно конкретной реализации ее в Java!) является выразительность, а не создание типизованных контейнеров. Типизованные контейнеры — всего лишь побочный эффект возможности создания универсального кода. Таким образом, хотя аргумент «Dog в списке Cat» часто используется для оправдания параметризации, этот аргумент спорен.

Из-за того, что параметризация была «встроена» в Java (а не проектировалась как составная часть языка с самого начала), некоторые контейнеры получились не такими мощными, как хотелось бы. Для примера взгляните на Map, особенно на методы containsKey(Object key) и get(Object key). Если бы эти классы проектировались в расчете на параметризацию, в этих методах вместо Object использовались бы параметризованные типы; тем самым обеспечивались бы необходимые проверки стадии компиляции. Скажем, в аналогичных контейнерах С++ тип ключа всегда проверяется во время компиляции.

Бесспорно, введение любого механизма параметризации в более позднюю версию языка, получившего широкое распространение, — крайне хлопотная затея. В С++ шаблоны были включены в исходную ISO-версию языка, так что они фактически всегда являлись его составной частью. В Java параметризация была введена лишь спустя 10 лет после выхода первой версии. Этот факт породил немало проблем с миграцией кода, а также оказал значительное влияние на архитектуру. В результате программисты страдают из-за близорукости, проявленной проектировщиками языка при создании версии 1.0. Конечно, при создании исходной версии они знали о шаблонах С++ и даже рассматривали возможность включения их в язык, но по тем или иным причинам решили этого не делать (скорее всего, просто торопились). В результате пострадал как язык, так и работающие на нем программисты. Только время покажет, как подход к параметризации в Java отразится на самом языке.

Массивы

В конце главы 5 было показано, как определить и инициализировать массив.

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

Особенности массивов

В Java существует немало разных способов хранения объектов, почему же массивы занимают особое место?

Массивы отличаются от других контейнеров по трем показателям: эффективность, типизация и возможность хранения примитивов. Массивы Java являются самым эффективным средством хранения и произвольного доступа к последовательности ссылок9 на объекты. Массив представляет собой простую линейную последовательность, благодаря чему обращения к элементам осуществляются чрезвычайно быстро. За скорость приходится расплачиваться тем, что размер объекта массива фиксируется и не может изменяться на протяжении его жизненного цикла. Как говорилось в главе 11, контейнер ArrayList способен автоматически выделять, дополнительное пространство, выделяя новый блок памяти и перемещая в него все ссылки из старого. Хотя обычно ArrayList отдается предпочтение перед массивами, за гибкость приходится расплачиваться, и ArrayList значительно уступает по эффективности обычному массиву.

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

Свадьба по приказу, или Моя непокорная княжна

Чернованова Валерия Михайловна
Любовные романы:
любовно-фантастические романы
5.57
рейтинг книги
Свадьба по приказу, или Моя непокорная княжна

Сборник коротких эротических рассказов

Коллектив авторов
Любовные романы:
эро литература
love action
7.25
рейтинг книги
Сборник коротких эротических рассказов

Отец моего жениха

Салах Алайна
Любовные романы:
современные любовные романы
7.79
рейтинг книги
Отец моего жениха

Вадбольский

Никитин Юрий Александрович
1. Вадбольский
Фантастика:
попаданцы
5.00
рейтинг книги
Вадбольский

Бастард Императора. Том 7

Орлов Андрей Юрьевич
7. Бастард Императора
Фантастика:
городское фэнтези
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Бастард Императора. Том 7

Повелитель механического легиона. Том VIII

Лисицин Евгений
8. Повелитель механического легиона
Фантастика:
технофэнтези
аниме
фэнтези
5.00
рейтинг книги
Повелитель механического легиона. Том VIII

В зоне особого внимания

Иванов Дмитрий
12. Девяностые
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
В зоне особого внимания

Таня Гроттер и магический контрабас

Емец Дмитрий Александрович
1. Таня Гроттер
Фантастика:
фэнтези
8.52
рейтинг книги
Таня Гроттер и магический контрабас

Бастард Императора. Том 2

Орлов Андрей Юрьевич
2. Бастард Императора
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Бастард Императора. Том 2

Кодекс Крови. Книга ХI

Борзых М.
11. РОС: Кодекс Крови
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Кодекс Крови. Книга ХI

Третий

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

Возвышение Меркурия. Книга 16

Кронос Александр
16. Меркурий
Фантастика:
попаданцы
аниме
5.00
рейтинг книги
Возвышение Меркурия. Книга 16

Идеальный мир для Лекаря 9

Сапфир Олег
9. Лекарь
Фантастика:
боевая фантастика
юмористическое фэнтези
6.00
рейтинг книги
Идеальный мир для Лекаря 9

Потусторонний. Книга 1

Погуляй Юрий Александрович
1. Господин Артемьев
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Потусторонний. Книга 1