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