Spec-Zone .ru
спецификации, руководства, описания, API
|
Это - самое спорное проектное решение в целом API. Ясно, статичный (время компиляции) проверка типа является очень требуемой, и является нормой в Java. Мы поддерживали бы это, если мы полагали, что это было выполнимо. К сожалению, попытки достигнуть этой цели вызывают взрыв в размере интерфейсной иерархии, и не преуспевают в том, чтобы избавить от необходимости исключения на этапе выполнения (хотя они уменьшают это существенно).
Дуг Lea, который записал популярный пакет наборов Java, который действительно отражал различия переменчивости в ее интерфейсной иерархии, больше не полагает, что это - жизнеспособный подход, основанный на пользовательском опыте с его пакетом наборов. В его словах (из персональной корреспонденции) "Очень, поскольку это причиняет боль мне, чтобы сказать это, сильный статический контроль типов не работает на интерфейсы набора в Java."
Чтобы иллюстрировать проблему в окровавленных деталях, предположите, что Вы хотите добавить понятие модифицируемости к Иерархии. Вы нуждаетесь в четырех новых интерфейсах: ModifiableCollection, ModifiableSet, ModifiableList, и ModifiableMap. То, что было ранее простой иерархией, является теперь грязным heterarchy. Кроме того, Вы нуждаетесь в новом интерфейсе Iterator для использования с неподдающимися изменению Наборами, которое не содержит удалить работу. Теперь можно покончить с UnsupportedOperationException? К сожалению, нет.
Рассмотрите массивы. Они реализуют большинство операций Списка, но не удаляют и добавляют. Они - Списки "фиксированного размера". Если Вы хотите получить это понятие в иерархии, необходимо добавить два новых интерфейса: VariableSizeList и VariableSizeMap. Вы не должны добавить VariableSizeCollection и VariableSizeSet, потому что они были бы идентичны ModifiableCollection и ModifiableSet, но Вы могли бы хотеть добавлять их так или иначе для пользы непротиворечивости. Кроме того, Вы нуждаетесь в новой разновидности ListIterator, который не поддерживает добавление и удаляет операции, чтобы согласиться с неподдающимся изменению Списком. Теперь мы - до десяти или двенадцать интерфейсов, плюс два новых интерфейса Iterator, вместо наших исходных четырех. Мы делаемся? Нет.
Рассмотрите журналы (такие как журналы ошибок, контролируйте журналы и журналы для восстанавливаемых объектов данных). Они - естественные только добавленные последовательности, та поддержка все операции Списка за исключением удаляют и устанавливают (заменяют). Они требуют нового базового интерфейса, и нового iterator.
И что относительно неизменных Наборов, в противоположность неподдающимся изменению? (то есть, Наборы, которые не могут быть изменены клиентом И никогда не будут изменяться ни по какой другой причине). Многие утверждают, что это - самое важное различие всех, потому что оно позволяет многократным потокам получать доступ к набору одновременно без потребности в синхронизации. Добавление этой поддержки иерархии типа требует еще четырех интерфейсов.
Теперь мы - приблизительно до двадцати интерфейсов и пять iterators, и почти бесспорно, что есть все еще наборы, возникающие практически, который не соответствует чисто ни одному из интерфейсов. Например, представления набора, возвращенные Картой, являются естественными только удаленными наборами. Кроме того, есть наборы, которые отклонят определенные элементы на основе их значения, таким образом, мы все еще не покончили с исключениями на этапе выполнения.
Когда все было сказано и сделано, мы чувствовали, что это был компромисс звукотехники, чтобы обойти целую проблему, обеспечивая очень маленький набор базовых интерфейсов, которые могут бросить исключение на этапе выполнения.
Это никогда не было наше намерение, что программы должны поймать эти исключения: именно поэтому они непроверены (время выполнения) исключения. Они должны только возникнуть в результате программирования ошибок, когда, Ваша программа остановится из-за непойманного исключения.
Интерфейс Набора обеспечивает эту функциональность. Мы не обеспечиваем общедоступных реализаций этого интерфейса, поскольку мы думаем, что он не использовался бы достаточно часто, чтобы "вытянуть его вес." Мы иногда возвращаем такие Наборы, которые реализуются легко на AbstractCollection (например, Набор, возвращенный Map.values).
В то время как имена новых методов наборов не придерживаются "Бобовых соглашений о присвоении имен", мы полагаем, что они являются разумными, непротиворечивыми и соответствующими их цели. Нужно помнить, что Бобовые соглашения о присвоении имен не применяются к JDK в целом; AWT действительно принимал эти соглашения, но то решение было несколько спорно. Мы подозреваем, что API наборов будут использоваться вполне глубоко, часто с многократными вызовами метода на одной строке кода, таким образом, будет важно, чтобы имена были коротки. Рассмотрите, например, методы Iterator. В настоящий момент цикл по набору похож на это:
for (Iterator i = c.iterator(); i.hasNext(); ) System.out.println(i.next());Все соответствует аккуратно на одной строке, даже если имя Набора является длинным выражением. Если бы мы назвали методы "getIterator", "hasNextElement" и "getNextElement", то это больше не имело бы место. Таким образом мы приняли "традиционный" стиль JDK, а не Бобовый стиль.
У многих реализаций Набора (включая всех тех предоставленных JDK) будет общедоступный метод клона, но это была бы ошибка потребовать этого всех Наборов. Например, что означает клонировать Набор это поддерживается базой данных SQL терабайта? Вызов метода должен заставить компанию реквизировать новую дисковую ферму? Подобные параметры содержат для сериализуемого.
Если клиент не знает фактический тип Набора, это намного более гибко и менее подвержено ошибкам, чтобы сделать, чтобы клиент решил, какой Набор требуется, создайте пустой Набор этого типа, и используйте addAll метод, чтобы скопировать элементы исходного набора в новый.
Это - то, что упоминается как "Внутренний Iterator" в книге "Шаблонов разработки" (Гамма и др.). Мы рассматривали, если это, но решил не к тому, поскольку это кажется несколько избыточным, чтобы поддерживать внутренний и внешний iterators, и у Java уже есть прецедент для внешнего iterators (с Перечислениями). "Вес броска" этой функциональности увеличивается фактом, что это требует, чтобы открытый интерфейс описал upcalls.
Легко реализовать эту функциональность на Iterators, и получающийся код может фактически выглядеть более чистым, поскольку пользователь может встроить предикат. Таким образом это не является четким, вытягивает ли это средство свой вес. Это могло быть добавлено к классу Наборов позднее (реализованный на Iterator), если это считало полезным.
Поскольку мы не верим в использование Перечислений (или Iterators) как "наборы плохого человека." Это иногда делалось в предшествующих выпусках, но теперь, когда у нас есть интерфейс Набора, это - привилегированный способ раздать абстрактные наборы объектов.
Снова, это - экземпляр Перечисления, служащего набором "плохого человека", и мы пытаемся препятствовать этому. Отметьте однако, что мы строго предлагаем, чтобы у всех конкретных реализаций были конструкторы, которые берут Набор (и создайте новый Набор с теми же самыми элементами).
Семантика неясна, учитывая, что контракт для Iterator не делает гарантий о порядке итерации. Отметьте, однако, что ListIterator действительно обеспечивает добавить работу, поскольку он действительно гарантирует порядок итерации.
Люди были равномерно разделены относительно того, предлагает ли Список связанные списки. Учитывая соглашение о присвоении имен реализации, <Реализация> <Интерфейс>, было сильное требование сохранить базовые интерфейсные имена короткими. Кроме того, несколько существующих имен (AbstractSequentialList, LinkedList) были бы решительно хуже, если мы изменили Список на Последовательность. С конфликтом имен может иметь дело следующее колдовство:
import java.util.*; import java.awt.*; import java.util.List; // Dictates interpretation of "List"
Было решено, чтобы "установить/получить" соглашение о присвоении имен строго достаточно хранилось на языке, который мы прикрепим с этим.
Это было проектом. Мы чувствуем, что отображения не являются наборами, и наборы не являются отображениями. Таким образом имеет небольшой смысл для Карты расширять интерфейс Набора (или наоборот).
Если Картой является Набор, каковы элементы? Единственным разумным ответом являются "Пары ключ/значение", но это обеспечивает очень ограниченный (и не особенно полезный) абстракция Карты. Невозможно спросить, что оценивает данный контурные карты, и при этом невозможно удалить запись для данного ключа, не зная, что оценивает ее, отображается на.
Набор мог быть сделан расширить Карту, но это повышает вопрос: каковы ключи? Нет никакого действительно удовлетворительного ответа, и принуждение того приводит к неестественному интерфейсу.
Карты могут быть просмотрены как Наборы (ключей, значений, или пар), и этот факт отражается в трех "Операциях представления набора" на Картах (набор ключей, entrySet, и значения). В то время как, в принципе, возможно просмотреть Список как Карту, отображающую индексы на элементы, у этого есть противное свойство, что удаление элемента от Списка изменяет Ключ, связанный с каждым элементом перед удаленным элементом. Именно поэтому у нас нет работы представления карты в Списках.
Мы просматриваем имена методов для Перечисления как неудачные. Они очень длинны, и очень часто используемые., Учитывая, что мы добавляли метод и создавали совершенно новую платформу, мы чувствовали, что будет глупо не использовать в своих интересах возможность улучшить имена. Конечно, мы могли поддерживать новые и старые названия в Iterator, но это не кажется стоящим.
Это может быть реализовано на текущем Iterators (подобный образец к java.io. PushbackInputStream). Мы полагаем, что его использование было бы достаточно редко, включая который это не стоит в интерфейсе, который все должны реализовать.
Если Вы исследуете цели на нашу платформу Наборов (в Кратком обзоре), то Вы будете видеть, что мы действительно "не играем в том же самом пространстве" как JGL. Заключение в кавычки от "Раздела" Целей Проекта Краткого обзора Наборов Java: "Наша основная цель проекта состояла в том, чтобы произвести API, который был разумно маленьким, и в размере, и (что еще более важно) в 'концептуальном весе.'"
JGL состоит приблизительно из 130 классов и интерфейсов; его основной целью была непротиворечивость со Стандартной библиотекой шаблонов C++ (STL). Это не было одной из наших целей. Java традиционно избежал C++ 's более сложные функции (например, множественное наследование, оператор, перегружающийся). Наша вся платформа, включая всю инфраструктуру, содержит приблизительно 25 классов и интерфейсы.
В то время как это может вызвать некоторый дискомфорт для некоторых программистов на C++, мы чувствуем, что это будет хорошо для Java в конечном счете. Поскольку библиотеки Java развиваются, они неизбежно растут, но мы пробуем столь же трудно, как мы можем, чтобы сохранить их маленькими и управляемыми, так, чтобы Java продолжал быть легким, забавным языком, чтобы изучить и использовать.
Учитывая, что мы обеспечиваем базовые интерфейсы набора, позади которых программисты могут "скрыть" свои собственные реализации, будут искаженные наборы, обеспечивает ли JDK их или нет. Устранение всех представлений от JDK значительно увеличило бы стоимость общих операций как создание Набора из массива, и покончит со многими полезными средствами (как синхронизирующиеся обертки). Одно представление, что мы видим как являющийся особенно полезным, является List.subList. Существование этого метода означает, что люди, которым методы записи, берущие Список на вводе, не должны записать вторичные формы, берущие смещение и длину (поскольку они делают для массивов).
Прежде всего, ограничения ресурса. Если мы собираемся передать такой API, это должно быть что-то, что работает на всех, что мы можем жить с для долгого пути. Однажды мы можем предоставить такую услугу. Тем временем не трудно реализовать такое средство сверху общедоступных API.