API Java-коллекций:почему неизменяемые [Список | Набор | Карта] не являются общедоступными классами?

StackOverflow https://stackoverflow.com/questions/4296619

Вопрос

Collections.unmodifiableList(...) возвращает новый экземпляр a статический внутренний класс UnmodifiableList.Другие неизменяемые классы коллекций создаются таким же образом.

Были эти классы были общедоступными, у каждого было два преимущества:

  • возможность указать более конкретное возвращаемое значение (например, UnmodifiableList), поэтому пользователю API не пришла бы в голову идея изменять эту коллекцию;
  • возможность проверить во время выполнения, есть ли List является instanceof UnmodifiableList.

Итак, были ли какие-то преимущества нет чтобы сделать эти классы общедоступными?

Редактировать:Никаких определенно убедительных аргументов представлено не было, поэтому я выбираю наиболее предпочтительный ответ.

Это было полезно?

Решение

Я думаю, что оба достопримечательности там, но не так полезны. Основными проблемами остаются прежними: UnmoDifialleList по-прежнему является списком, и поэтому все посетители доступны, и основные коллекции все еще могут быть модифицированы. Создание общественности класса UnmoDifiableList добавит к иллюзии быть немодифицируемым.

Ницкий путь будет для компилятора, чтобы помочь, но для того, чтобы иерархии класса коллекции должны были бы многое изменить. Например, API сбор SCALA намного продвинута в этом отношении.

Недостатком будет введение не менее трех дополнительных классов / интерфейсов в API. Из-за них не будучи полезным, я думаю, что оставив их из API, это хороший выбор.

Другие советы

Лично я полностью с вами согласен.В основе проблемы лежит тот факт, что дженерики Java не являются ковариантными, что, в свою очередь, связано с тем, что коллекции Java изменчивы.

Система типов Java не может кодифицировать тип, который, по-видимому, имеет мутаторы является фактически неизменяемый.Представьте, если бы мы начали разрабатывать какое-то решение:

interface Immutable //marker for immutability

interface ImmutableMap<K, V> extends Map<K, V>, Immutable

Но тогда ImmutableMap является подклассом Map, и , следовательно , Map может быть присвоен из ImmutableMap итак, любой метод, который возвращает такую неизменяемую карту:

public ImmutableMap<K, V> foo();

может быть присвоен Карте и, следовательно, может быть изменен во время компиляции:

Map<K, V> m = foo();
m.put(k, v); //oh dear

Итак, вы можете видеть, что добавление этого типа на самом деле не помешало нам сделать что-то плохое.Я думаю, по этой причине было принято решение, что ему нечего предложить.


Такой язык , как скала имеет аннотации к отклонениям на сайте объявления.То есть вы могли бы указать тип как ковариантный (и, следовательно, неизменяемый), как и карта Scala (на самом деле она ковариантна в своем V параметр).Следовательно, ваш API может объявлять, является ли его возвращаемый тип изменяемым или неизменяемым.

Кроме того, Scala позволяет вам объявлять типы пересечений, так что вам даже не нужно создавать ImmutableXYZ интерфейс как отдельная сущность, вы могли бы указать метод для возврата:

def foo : XYZ with Immutable

Но тогда scala имеет правильную систему типов, в то время как Java ее нет

Если для вас важно проверить, был ли список создан с помощью Collections.unmodifiableList, вы можете создать экземпляр и запрашивать класс. Теперь вы можете сравнить этот класс с классом любого списка.

private static Class UNMODIFIABLE_LIST_CLASS = 
    Collections.unmodifiableList( new ArrayList() ).getClass();
...
if( UNMODIFIABLE_LIST_CLASS == listToTest.getClass() ){
    ...
} 

Ответ на почему довольно просто: в то время, в 1998 году, эффективный дизайн был немного фланкой. Люди думали об этом, это явно не был приоритетом. Но не было истинного, глубокого мышления об этом.

Если вы хотите использовать такой механизм, используйте ImmutableList Guava / Set / Map / ...

Они явно неизменны и хорошая практика при использовании этой библиотеки - не возвращать список, например, но неизменяемого. Таким образом, вы будете знать, что список / Set / Map / ... неизменен.

Пример:

private final ImmutableList constants = ...;
public final ImmutableList<String> getConstants() {
  return constants;
}

О самом дизайне о немодируемомXXXX, можно было бы сделать следующее:

public static final class UnmodifiableXxx implements Xxx { // don't allow extend
  // static if inside Collections
  UnmodifiableXxx (Xxx backend) { // don't allow direct instanciation
    ...
  }
  ...
}
  • возможность указывать более конкретное возвращаемое значение (например, UnmodifiableList), поэтому пользователь API не придет к идее модификации этой коллекции;

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

  • Возможность проверки во время выполнения, если List является instanceof UnmodifiableList.

Такая потребность указывает на то, что действительный Проблема лежит где-то еще. Это недостаток в дизайне кода. Спросите себя, вы когда-нибудь имели необходимость проверить, List является примером ArrayList или LinkedList? Будь то ArrayList, LinkedList или UnmodifiableList Ясно, что решение, которое должно быть сделано во время записи кода, не во время выполнения кода. Если вы столкнулись с проблемами, потому что вы пытаетесь изменить UnmodifiableList (Для которого разработчик API может иметь очень хорошую в голову, которая должна быть уже задокументирована), то это скорее собственная ошибка, а не ошибка выполнения.

Все со всеми, это не имеет смысла. То Collections#unmodifiableXXX(), synchronizedXXX() и checkedXXX() делать никак не представлять конкретные реализации. Все они просто декораторы которые могут быть применены независимо от основной конкретной реализации.

Предполагать UnmodifiableList был Открытый класс. Я подозреваю, что он убают программисты в ложном чувстве безопасности. Помните, UnmodifiableList это Посмотреть а. модифицировано List. Отказ Это означает, что содержимое UnmodifiableList все еще может измениться с помощью любых изменений, внесенных в его основе List. Отказ Наивный программист может не понять этот нюанс и может ожидать, что экземпляры UnmodifiableList быть неизменным.

Я думаю, что ответ заключается в том, что форма метода правильно знает о используемых универсальных значениях и не требует дополнительного программирования для передачи этой информации, в то время как форма класса потребует больше возиться. Форма метода для unmodifiableMap Имеет две плавающие общие аргументы, которые он отображает как об общих аргументах возвращаемого типа и прошедшего аргумента.

public static <K,V> Map<K,V> unmodifiableMap(Map<? extends K, ? extends V> m) {
    return new UnmodifiableMap<K,V>(m);
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top