Использовать интерфейс или тип для определения переменных в Java? - PullRequest
14 голосов
/ 22 сентября 2010
ArrayList aList = new ArrayList();

List aList = new ArrayList();

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

Ответы [ 8 ]

21 голосов
/ 22 сентября 2010

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

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

7 голосов
/ 22 сентября 2010

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

Должно быть:

List<String> list = new ArrayList<String>();
// or whatever data type you are using in your list

Пожалуйста, прочитайте Эффективная Java Джошуа Блоха, особенно эти два пункта:

  • 23: не используйте необработанные типы в новом коде (это даже доступно онлайн )
  • 52: обращаться к объектам по их интерфейсы

Кстати, если вы используете Guava , у вас есть фабричный метод для построения ArrayList, поэтому вам не нужно повторять параметр типа:

List<String> list = Lists.newArraylist();
5 голосов
/ 22 сентября 2010

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

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

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

 void showAll(List <String> sl) ...

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

 void showAllAS(ArrayList <String> sl) ...

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

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

2 голосов
/ 23 сентября 2010

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

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

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

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

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

1 голос
/ 31 августа 2015

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

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

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

См. Effective Java для стандартных рекомендаций; следуют личные мысли.

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

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

  • Вам нужны члены, присутствующие только в подтипе, например:
    • некоторая особенность реализации, например ensureCapacity для ArrayList<T>
    • (обычно в тестовом коде) некоторый член фальшивого класса, например (гипотетически) FakeFileSystem#createFakeFile.
  • Вы полагаетесь на поведение подтипа, особенно в переопределениях методов супертипа, таких как:
    • с более общим типом параметра,
    • более конкретный тип возврата или
    • выбрасывает более конкретное или меньшее количество типов исключений.

В качестве примера последнего см. Должен ли я закрыть StringReader? : StringReader.html # close переопределяет Reader.html # close и выполняет not выдает IOException, поэтому использование StringReader вместо Reader для локальной переменной означает, что вам не нужно обрабатывать исключение, которое на самом деле не может произойти, и значительно сокращает шаблон.

1 голос
/ 22 сентября 2010

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

0 голосов
/ 22 сентября 2010

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

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

Map<String, Object> map = new HashMap<String, Object>();

public void test(Map<String, Object> m) {...};

Method m = this.getClass().getMethod("test", new Class<?>[]{map.getClass()});

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

0 голосов
/ 22 сентября 2010

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

Начиная с 1.5, я бы пошел со следующим

List<String> x = new ArrayList<String>();

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

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

@SuppressWarnings("unchecked")
List y = new ArrayList();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...