Как правило, вы должны предпочитать интерфейсы конкретным классам. Кроме того, если вы можете избежать использования оператора new (для которого всегда требуется конкретный тип, как в вашем новом примере ArrayList), еще лучше.
Все это связано с управлением зависимостями в вашем коде. Лучше полагаться только на очень абстрактные вещи (например, интерфейсы), потому что они также имеют тенденцию быть очень стабильными (см. http://objectmentor.com/resources/articles/stability.pdf). Поскольку у них нет кода, их нужно менять только при изменении API ... другими словами когда вы хотите, чтобы этот интерфейс представлял другое поведение миру, т. е. изменение дизайна.
Классы, с другой стороны, меняются все время. Код, который зависит от класса, не заботится о том, как он делает то, что он делает, до тех пор, пока входные и выходные данные API не изменяются, вызывающие не должны заботиться.
Вы должны стремиться закрепить поведение ваших классов в соответствии с принципом Открыто-Закрыто (см. http://objectmentor.com/resources/articles/ocp.pdf),, чтобы существующие интерфейсы не менялись, даже когда вы добавляете функциональность, вы можете просто указать новый подинтерфейс.
Старый способ избежать использования нового оператора заключался в использовании шаблона абстрактной фабрики, но у него есть свои проблемы. Лучше использовать такой инструмент, как Guice, который выполняет внедрение зависимостей и предпочитает внедрение конструкторов. Убедитесь, что вы понимаете принцип обращения зависимостей (см. http://objectmentor.com/resources/articles/dip.pdf), прежде чем начать использовать внедрение зависимостей. Я видел, как многие люди вводят неподходящие зависимости, а потом жалуются, что инструмент им не помогает ... это не сделает вас хорошим программистом, вы все равно должны использовать его надлежащим образом.
Пример: вы пишете программу, которая помогает студентам изучать физику. В этой программе учащиеся могут поместить мяч в различные физические сценарии и посмотреть, как он себя ведет: выстрелить из пушки со скалы, положить его под воду, в дальний космос и т. Д. Вопрос: вы хотите включить что-нибудь о тяжести мяч в Ball API ... следует ли вам включить метод getMass () или метод getWeight ()?
Вес зависит от среды, в которой находится мяч. Для вызывающих абонентов может быть удобно вызывать один метод и получать вес шара, где бы он ни находился, но как вы пишете этот метод? Каждый экземпляр шара должен постоянно отслеживать, где он находится и какова текущая гравитационная постоянная. Поэтому вы должны предпочесть getMass (), потому что масса является внутренним свойством шара и не зависит от его окружения.
Подождите, а что если вы просто используете вместо этого getWeight (Environment)? Таким образом, экземпляр ball может просто получить свой текущий g из окружения и продолжить ... еще лучше, вы можете использовать Guice для добавления окружения в конструктор Ball! Такого рода злоупотребления я часто вижу, и в итоге люди обвиняют Guice в том, что он не в состоянии справиться с внедрением зависимостей так легко, как они надеялись.
Проблема здесь не в Guice, а в дизайне Ball API. Вес не является внутренним свойством мяча, поэтому это не свойство, которое должно быть доступно с мяча. Вместо этого Ball должен реализовать интерфейс MassiveObject с помощью метода getMass (), а в среде должен быть метод getWeightOf (MassiveObject). Природой для Окружающей среды является ее собственная гравитационная постоянная, так что это намного лучше. Среда теперь зависит только от простого интерфейса, MassiveObject ... но его задача состоит в том, чтобы содержать объекты, поэтому так и должно быть.