Почему перечисление преобразуется в ArrayList, а не в список на java.utils?


есть ли веская причина, что коллекций.list () метод возвращает ArrayList<T> вместо List<T>?

очевидно ArrayList - это List, но у меня сложилось впечатление, что обычно рекомендуется возвращать тип интерфейса вместо типа реализации.

4 74

4 ответа:

отказ от ответственности: я не автор JDK.

Я согласен, что правильно писать ваш код к интерфейсам, но если вы собираетесь вернуть a mutable коллекция для третьей стороны, важно, чтобы третья сторона знала, что это за List они снова.

LinkedList и ArrayList очень разные, производительность мудрая, для различных операций. Например, удаление первого элемента ArrayList - это O(n), но удаление первого элемента LinkedList и O(1).

полностью указав тип возвращаемого значения, авторы JDK являются передача дополнительной информации, в однозначном коде, о том, какой объект они возвращают вам, поэтому вы можете написать свой код, чтобы правильно использовать этот метод. Если вам действительно нужно LinkedList, вы знаете, что вы должны указать один здесь.

наконец, основная причина кодирования интерфейса над реализацией-это если вы думаете, что реализация изменится. Авторы JDK, вероятно, считают, что они никогда собирается изменить этот метод; он никогда не будет возвращать LinkedList или Collections.UnmodifiableList. Однако в большинстве случаев вы, вероятно, все равно сделаете:

List<T> list = Collections.list(enumeration);

при возврате List, вы будете продвижения программа для интерфейс, который является очень хорошей практикой. Однако этот подход имеет свои ограничения. Например, нельзя использовать некоторые методы, определенные для ArrayList и не существует в List интерфейс - см. ответ для сведения.

я цитирую API Design из учебников Java™:

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

.. В каком-то смысле, возвращаемые значения должны иметь противоположное поведение входных параметров: это лучшие вернуть наиболее конкретный применимый интерфейс коллекции, а не самый общий. Например, если вы уверены, что всегда будете возвращать SortedMap, вы должны дать соответствующий метод тип возвращаемого SortedMap, а не Map. SortedMap создание экземпляров занимает больше времени, чем обычные Map экземпляры, а также более мощные. Учитывая, что ваш модуль уже вложил время в создание SortedMap, имеет смысл дать пользователю доступ к его повышенной мощности. Кроме того, пользователь сможет передать возвращаемый объект методам, которые требуют a SortedMap, а также как те, что принимают любые Map.

С ArrayList по сути является массивом, они являются моим первым выбором, когда мне нужно иметь "collection-array". Поэтому, если я хочу преобразовать перечисление в список, моим выбором будет список массивов.

в любых других случаях все еще допустимо писать:

List<T> list = Collections.list(e);

функции, которые возвращают "исключительное право собственности" на вновь созданные изменяемые объекты, часто должны быть наиболее конкретным типом; те, которые возвращают неизменяемые объекты, особенно если они могут быть разделены, часто должны возвращать менее конкретные типы.

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

в последнем случае тот факт, что объект является неизменяемым, означает, что функция может идентифицировать альтернативный тип, который может делать все, что может сделать более сложный тип учитывая его точное содержание. Например,Immutable2dMatrix интерфейс может быть реализован ImmutableArrayBacked2dMatrix класс а ImmutableDiagonal2dMatrix класса. Функция, которая должна возвращать площадью Immutable2dMatrix может решить вернуть a ImmutableDiagonalMatrix например, если все элементы от главной диагонали оказываются равными нулю, или ImmutableArrayBackedMatrix если не. Первый тип займет гораздо меньше места для хранения, но получатель не должен заботиться о разнице между ними.

возвращение Immutable2dMatrix, а не конкретный ImmutableArrayBackedMatrix позволяет коду выбрать тип возврата на основе того, что содержит массив; он также означает, что если код, который должен возвращать массив, содержит подходящую реализацию Immutable2dMatrix Он может просто вернуть это, а не создавать новый экземпляр. Оба эти фактора могут быть основными "выигрышами" при работе с неизменяемыми объектами.

при работе с изменяемыми объектами, однако ни один фактор вступает в игру. Тот факт, что изменяемый массив может не иметь никаких элементов от главной диагонали, когда он генерируется, не означает, что он никогда не будет есть такие элементы. Следовательно, в то время как ImmutableDiagonalMatrix фактически является подтипом Immutable2dMatrix, a MutableDiagonalMatrix не является подтипом a Mutable2dMatrix, так как последний может принимать магазины от главной диагонали, в то время как первый не может. Кроме того, в то время как неизменяемые объекты часто могут и должны совместно использоваться, изменяемые объекты обычно не могут. Функция, которая запрашивается для новой изменяемой коллекции, инициализированной определенным содержимым, должна будет создать новую коллекцию независимо от того, соответствует ли ее резервное хранилище запрашиваемый тип.

есть маленький накладные расходы при вызове методов var интерфейс, а не непосредственно на объекте.

эти накладные расходы часто не более 1 или 2 инструкций процессора. Накладные расходы на вызов метода еще ниже, если компилятор знает, что метод является окончательным. Это не измеримо для большинства кода Вы и я правы, но для методов низкого уровня в java.утилиты могут быть использованы в коде, где это является проблемой.

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

очевидно, авторы java.utils не имеют никакого способа узнать, что все программное обеспечение, которое вызывает коллекции.list () делает с результатом и никаким способом повторно протестировать это программное обеспечение, если они изменяют имплантацию коллекций.список.)( поэтому они не собираются менять имплантацию коллекций.list () для возврата другого типа списка, даже если система типов разрешила это!

при написании собственного программного обеспечения, вы (надеюсь) автоматизированный тест, который охватывает весь ваш код и хорошо понять, как ваш код взаимосвязей включают знать, где производительность является проблемой. Будучи в состоянии внести изменения в метод, без необходимости изменения вызывающих абонентов имеет большое значение в то время как дизайн программное обеспечение меняется.

поэтому два набора компромиссов очень разные.