Использовать интерфейс или тип для определения переменных в Java?
=================================================================

В чем разница между этими двумя и что лучше использовать и почему?

`List<T>` это интерфейс, где`ArrayList<T>` это класс, и этот класс реализует`List<T>` интерфейс.

Я бы предпочел 2-ую форму, которая является более общей, т.е. если вы не используете методы, специфичные для`ArrayList<T>` Вы можете объявить его тип в качестве интерфейса`List<T>` тип. Со 2-й формой легче будет изменить реализацию с`ArrayList<T>` в другой класс, который реализует`List<T>` интерфейс.

**РЕДАКТИРОВАТЬ:** Как прокомментировали многие пользователи SO, обе формы могут быть аргументами любого метода, который принимает`List<T>` или же`ArrrayList<T>`, Но когда я объявляю метод, я бы предпочел интерфейс:

и использовать:

только когда мой метод использует метод, специфичный для`ArrayList<T>`, лайк`ensureCapacity()`.

Ответ с информацией, которую мы должны использовать напечатано`List<T>` вместо просто`List` это очень хорошо (конечно, если мы не используем древнюю Java).



Все эти ответы повторяют некоторые догмы, которые они где-то читали.

Объявление переменной и оператор инициализации,`Type x = new Constructor();`, безусловно, является частью детализации реализации. Это не часть публичного API (если только`public final`, но`List` изменчиво, так что это неуместно)

В качестве детали реализации, кого вы пытаетесь обмануть своими абстракциями? Лучше, чтобы тип был как можно более конкретным. Это список массивов или связанный список? Это должно быть потокобезопасным или нет? Выбор важен для вашей реализации, вы тщательно выбрали конкретный список. Тогда вы объявляете это просто`List` как будто это не имеет значения, и вам все равно?

Единственная законная причина объявить это`List` являетсяМне лень набирать, Это также охватывает аргумент, чтоесли мне нужно перейти в другой список, то есть у меня меньше места для изменения.

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



Это причуды Java, из-за ограниченного вывода типа и ООП доктрины. Для локальных переменных это в основном стиль; если вам нужна какая-то конкретная особенность подтипа, используйте подтип (примеры ниже), но в остальном можно использовать любой из них.

Стиль Java состоит в том, чтобы использовать супертип, применять интерфейсы даже в теле реализаций и обеспечивать согласованность с видимыми типами (Эффективная Java 2-е издание: пункт 52: ссылки на объекты по их интерфейсам). В языках с большим количеством выводов типов, таких как C ++ / C # / Go / и т. Д., Вам не нужно явно указывать тип, и локальная переменная будет иметьконкретный тип.

Для видимых типов (public или protected: поля или параметры и возвращаемое значение методов) вы почти всегда хотите использовать самый общий тип: интерфейс или абстрактный класс, чтобы обеспечить большую гибкость (Эффективная Java: Пункт 40: метод проектирования подписей тщательно). Однако для типов, которые не видны (частные или закрытые для пакета члены, или локальные переменные), можно использовать любой (это просто стиль), а иногда необходимо использовать более конкретные типы, включая конкретные классы.

УвидетьЭффективная Java для стандартных руководящих принципов; личные мысли следуют.

Причина использования более общих типов, даже если они не видны, заключается в уменьшении шума: вы заявляете, что вам нужен только более общий тип. Использование общих типов на элементах, которые не видны (например, закрытые методы), также уменьшает отток пользователей при изменении типов. Однако это не относится к локальным переменным, где в любом случае просто меняется одна строка:`ConcreteFoo foo = new ConcreteFoo();` в`OtherConcreteFoo foo = new OtherConcreteFoo();`.

Случаи, когда выделать Нужно ли включать в подтип:

Вам нужны члены, присутствующие только в подтипе, например:некоторые особенности реализации, такие как`ensureCapacity` за`ArrayList<T>`(обычно в тестовом коде) какой-то член фальшивого класса, вроде (гипотетически)`FakeFileSystem#createFakeFile`.Вы полагаетесь на поведение подтипа, особенно в переопределениях методов супертипа, таких как:имеющий более общий тип параметра,более конкретный тип возврата, илибросать более конкретные или меньше типов исключений.В качестве примера последнего см.[Должен ли я закрыть StringReader?](https://stackoverflow.com/questions/6122013/should-i-close-a-stringreader): [StringReader.html # близко](https://docs.oracle.com/javase/7/docs/api/java/io/StringReader.html#close()) Переопределение[Reader.html # близко](https://docs.oracle.com/javase/7/docs/api/java/io/Reader.html#close()) и делаетне бросить`IOException`так что используя`StringReader` вместо`Reader` для локальной переменной означает, что вам не нужно обрабатывать исключение, которое на самом деле не может произойти, и значительно сокращает шаблон.



Если вы используете Java 1.4 или более раннюю версию, то я бы использовал второй. Всегда лучше объявлять ваши поля как можно более обобщенно, на случай, если позже вам понадобится преобразовать их во что-то другое, например, в вектор и т. Д.

С 1.5 я бы пошел со следующим

Это дает вам немного безопасности типов. Список «x» может добавить объект String, и все. Когда вы получаете элемент из списка 'x', вы можете рассчитывать на то, что строка вернется. Это также помогает, удаляя ненужное приведение, что может затруднить чтение кода, когда вы вернетесь через 6 месяцев назад и попытаетесь вспомнить, что делает ваш код. Ваш компилятор / IDE поможет вам вспомнить, какой тип должен входить в Список 'x', отображая ошибку, если вы пытаетесь добавить любой другой тип объекта.

Если вы хотите добавить несколько типов объектов в список, вы можете добавить аннотацию для подавления ошибки компиляции.



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

Я сделал исправление ошибки в некотором коде, где я объявил переменную как`Map<String, MyObject>` и назначил ему экземпляр`HashMap<String, MyObject>`, Эта переменная использовалась в качестве параметра в вызове метода, доступ к которому осуществлялся посредством отражения. Проблема в том, что рефлексия пыталась найти метод с`HashMap` подпись а не заявленная`Map` подпись. Так как не было никакого метода с`HashMap` в качестве параметра я не смог найти метод по отражению.

Будет**не** найти метод, который использует интерфейс. Если вы делаете другую версию теста, которая использует`HashMap` вместо этого тогда это будет работать - но теперь вы вынуждены объявить вашу переменную с конкретным классом, а не с более гибким интерфейсом ...



List - это интерфейс, а ArrayList - реализация этого интерфейса.

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



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



Оба устарели начиная с Java 1.5.

Так должно быть:

Пожалуйста, прочитайте[Эффективная Java](http://java.sun.com/docs/books/effective/) Джошуа Блох, особенно эти два пункта:

23: не используйте необработанные типы в новом коде (это даже[доступно онлайн](http://java.sun.com/docs/books/effective/generics.pdf))52: ссылаются на объекты по их интерфейсамКстати, если вы используете[гуайява](https://code.google.com/p/guava-libraries), у тебя есть[заводской метод](https://guava-libraries.googlecode.com/svn/trunk/javadoc/com/google/common/collect/Lists.html#newArrayList%28%29) для создания ArrayList, так что вам не нужно повторять параметр type: