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

ArrayList aList = new ArrayList();

List aList = new ArrayList();

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

 irreputable23 сент. 2010 г., 00:24
правильный ответ, если есть такая вещь, это 1-я форма. см. мой ответ ниже.

Ответы на вопрос(8)

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

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

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

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

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

 sleske09 февр. 2011 г., 13:52
Кстати, я не согласен с тем, что выбор важен для вашей реализации, вы тщательно выбрали конкретный список. , Например, когда используя классы коллекций, я почти всегда просто использую ArrayList & HashSet, и только пересматриваю, если это вызывает проблемы (то есть практически никогда). В этом случае использование List / Set лучше выражает мойнамерениет.е. я просто хочу список.
 sleske09 февр. 2011 г., 13:51
Я в основном не согласен с этим, но это не заслуживает -3; голосование, потому что это вызывает веские доводы (хотя я не согласен с заключением).
 maba10 окт. 2012 г., 09:42
+1; Я полностью согласен с этим. Если область действия мала, используйте конкретный тип. Я был брошен здесь от вашего другого ответа:stackoverflow.com/a/12805778/1350762

Это причуды 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?: StringReader.html # близко ПереопределениеReader.html # близко и делаетне броситьIOExceptionтак что используяStringReader вместоReader для локальной переменной означает, что вам не нужно обрабатывать исключение, которое на самом деле не может произойти, и значительно сокращает шаблон.

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

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

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

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

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

@SuppressWarnings("unchecked")
List y = new ArrayList();
 Sean Patrick Floyd22 сент. 2010 г., 15:10
Это плохой стиль. Не подавляйте предупреждения, используйтеList<Object>, Пример:List<Object> y = new ArrayList<Object>(); y.add(1); y.add("abc"); y.add(true);
 Tony Ennis22 сент. 2010 г., 15:52
Если вы используете 1.4 или более раннюю версию, первое, что нужно сделать, - это обновить JVM.
 Sean Patrick Floyd22 сент. 2010 г., 15:10
(Автобокс, который я использовал в моем примере, тоже, конечно, плохой стиль)
 bakoyaro24 сент. 2010 г., 21:27
Я не хочу начинать войну пламенем, я просто хочу дать оправдание для того, чтобы оставить ссылку на Java 1.4. Я уверен, что есть, по крайней мере, еще один плохой разработчик, у которого нет выбора, кроме как использовать меньше, чем самые современные инструменты, для поддержки старого продукта. Прискорбно, что у некоторых из нас нет другого выхода, кроме как продолжать использовать старую JVM в больших устаревших проектах, потому что бизнес будет разрешать только обновления пользовательского кода, но не поддержку библиотек. Существует также обоснование затрат на повторное тестирование всей базы кода, что может привести к использованию старой JVM.

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

Я сделал исправление ошибки в некотором коде, где я объявил переменную как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 вместо этого тогда это будет работать - но теперь вы вынуждены объявить вашу переменную с конкретным классом, а не с более гибким интерфейсом ...

 BigMac6623 сент. 2010 г., 15:35
@ Джон выглядит как маленькая опечатка, но это больше, чем это. Вся цель использования отражения в данном конкретном случае состоит в том, что я не знаю, какой метод вызывать. Я передал список <Object> для параметров. Если бы я знал, что хочу использовать метод с параметром Map <>, я бы не использовал отражение :)
 Jorn23 сент. 2010 г., 00:32
Фактическая ошибка здесь заключается в том, что вы используете map.getClass (), которая будет возвращать HashMap.class, для поиска метода, в то время как вы должны использовать Map.class.
 irreputable22 сент. 2010 г., 23:59
ошибка в коде поиска метода. сложный поиск не потребует точного соответствия типа параметра, вместо этого он моделирует алгоритм компилятора для поиска подходящего метода.

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

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

 Ben30 авг. 2011 г., 01:55
+1 Стричь и высушить. Изучая тему и пожелание, я нашел это раньше, потому что эти слова ответили на большее количество вопросов, чем на тысячу раздутых повторяющихся очерков по этому вопросу. </ Poeticlicense>
 Bill Michell22 сент. 2010 г., 14:51
Именно так. Если вам когда-нибудь понадобится убедиться, что вы работаете с ArrayList позже, вы можете изменить объявление. В противном случае остальной части вашего кода на самом деле не нужно знать, какой тип List используется, так что вы можете поменяться на другую реализацию позже (возможно, LinkedList), если производительность или другие соображения оправдывают это.
 Jorn23 сент. 2010 г., 00:25
@ILMTitan, не говоря уже о других коллекциях, таких как неизменные из коллекций Google.
 ILMTitan22 сент. 2010 г., 16:37
@Bill каждый всегда упоминает LinkedList при реализации, которую вы тоже можете переключить, но я думаю, что ситуация становится сильнее, если вы упомянете Collections.unmodifiableList (), Collections.checkedList () или Collections.synchronizedList ().

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

Решение Вопроса

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

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

 Ishtar22 сент. 2010 г., 14:47
@Roalt - я прокомментировал, потому что я надеюсь, что Михал Никлас исправит свой ответ, чтобы он не был неправильно понят, и это будет хороший ответ! Но в его нынешнем состоянии этоявляется неправильно.
 Roalt22 сент. 2010 г., 15:46
@Bert F: Если бы он сказал «... вы можете передать List (или ArrayList) методам, которые работают с параметром List ...», было бы меньше путаницы, верно? Я вижу, что теперь это не правильно.
 Bert F22 сент. 2010 г., 15:30
@Roalt - я уверен, что у @Michal все правильно, но в ответ получилось неправильно. Первоначальное понятие верно («2-я форма ... является более общим»), но остальное явно неверно. Так что это место для ответов и правильной информации. Снижение голосов и комментарии гарантируют, что (принятый!) Ответ с неправильной информацией не просто принят за чистую монету. Пониженные голоса (и, возможно, комментарии) могут быть удалены после исправления ответа.
 Ishtar22 сент. 2010 г., 14:14
-1 Всё наоборот; Вы можете передатьArrayList к методам, которые хотятList, Вы не можете передатьList к методу, требующемуArrayList.
 Jorn23 сент. 2010 г., 00:23
Я могу себе представить ... напомните мне еще раз, почему один человек получает право вето всему сообществу, на какой ответ лучше? Я не против возможности принимать ответы, но принятый ответ не должен быть на вершине.
 Sean Patrick Floyd22 сент. 2010 г., 14:28
@ Иштар, Берт, точно. Но вы забыли упомянуть, что метод почти никогда не должен ожидать ArrayList. В 99% случаев подойдет любой Список и в 50% любая Коллекция.
 Michał Niklas23 сент. 2010 г., 07:05
Спасибо за комментарии. Вы правы, я больше думал о том, чтобы объявить метод, а не переменные. Я надеюсь, что мой отредактированный ответ лучше.
 Rich22 сент. 2010 г., 15:50
Это все очень расстраивает с моей точки зрения девяти голосов: P
 Roalt22 сент. 2010 г., 14:39
+1 Я думаю, что Михала здесь немного неправильно поняли. Если вы создаете интерфейс, лучше использовать List, если внутри не используются специфические функции ArrayList. Таким образом, вы не навязываете реализацию пользователю. ArrayList является одной из возможных реализаций List.
 Bert F22 сент. 2010 г., 14:09
Это звучит для меня задом наперед (неправильно). 1-й классArrayList aList может быть передан в качестве аргумента любому методу, который принимаетList или жеArrayList параметр. 2-й классList aList может быть передан в качестве аргумента только методам, которые принимаютList параметр. Хотя переменнаяList aList ссылается наArrayListстрогая типизация помешает ссылочной переменнойaList от сопоставления в качестве аргументаArrayList параметр.

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

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

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

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

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

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

List<String> list = Lists.newArraylist();
 dertoni22 сент. 2010 г., 14:44
+1 за «Эффективную Яву», это действительно хорошая книга.
 Jorn23 сент. 2010 г., 00:26
Я бы сказал, что этопервый книгу, которую должен прочитать каждый разработчик Java;)
 Sean Patrick Floyd22 сент. 2010 г., 14:47
@ Дертони Я согласен, это единственныйодин книга, которую должен прочитать каждый разработчик Java.
 Roalt22 сент. 2010 г., 14:43
Я надеюсь, что все знают, что вы должны использовать шаблон-версию List уже сейчас (например, eclipse будет вас раздражать, если вы этого не сделаете). Но ваше второе замечание о Гуаве довольно интересно!

Ваш ответ на вопрос