Java - объявление от Интерфейсного типа вместо Класса

В моих поисках для корректного схватывания Интерфейсных лучших практик я заметил объявления, такие как:

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

вместо

ArrayList<String> myList = new ArrayList<String>();

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

С этой логикой я настроил пример:

public class InterfaceTest {

    public static void main(String[] args) {

        PetInterface p = new Cat();
        p.talk();

    }

}

interface PetInterface {                

    public void talk();

}

class Dog implements PetInterface {

    @Override
    public void talk() {
        System.out.println("Bark!");
    }

}

class Cat implements PetInterface {

    @Override
    public void talk() {
        System.out.println("Meow!");
    }

    public void batheSelf() {
        System.out.println("Cat bathing");
    }

}

Мой вопрос, я не могу получить доступ к batheSelf () метод, потому что он только существует для CAT. Это приводит меня полагать, что я должен только объявить от Интерфейса, если я только собираюсь использовать методы, объявленные в Интерфейсе (и не дополнительные методы от подкласса), иначе я должен объявить от Класса непосредственно (в этом CAT случая). Я корректен в этом предположении?

38
задан skaffman 10 December 2010 в 23:49
поделиться

5 ответов

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

Рассмотрим String в качестве примера реализует CharSequence . Вы не должны просто слепо использовать CharSequence вместо String во всех случаях, потому что это лишит вас простых операций, таких как trim () , toUpperCase ( ) и т. Д.

Однако метод, который принимает String только для того, чтобы заботиться о своей последовательности char значений , должен использовать CharSequence вместо этого, потому что это подходящий тип в данном случае. Фактически, это имеет место при замене (цель CharSequence, замена CharSequence) в классе String .

Другой пример - java.util.regex.Pattern и его метод сопоставления Matcher (CharSequence) . Это позволяет создать Matcher из Pattern не только для String , но и для всех других CharSequence , которые существуют.

Отличный пример в библиотеке, где должен был использоваться интерфейс , но, к сожалению, не использовался, также можно найти в Matcher : его appendReplacement и appendTail методы принимают только StringBuffer . Этот класс был в значительной степени заменен его более быстрым родственником StringBuilder , начиная с версии 1.5.

StringBuilder не является StringBuffer , поэтому мы не можем использовать первый с методами append… в Matcher . Однако оба они реализуют Appendable (также введено в 1.5). В идеале Matcher метод append… должен принимать любой добавляемый , и тогда мы могли бы использовать StringBuilder , а также все другой Добавляемый доступен!

Итак, мы можем видеть, как , когда существует соответствующий тип , обращение к объектам через их интерфейсы может быть мощной абстракцией, но только если эти типы существуют. Если тип не существует, вы можете рассмотреть возможность определения одного из них, если это имеет смысл. В этом примере Cat вы можете определить, например, интерфейс SelfBathable . Затем вместо ссылки на Cat вы можете принять любой объект SelfBathable (например, Parakeet )

Если нет смысла создавать новый type, то вы непременно можете ссылаться на него по его классу .

См. Также

  • Эффективное 2-е издание Java, Правило 52: Обращайтесь к объектам по их интерфейсам

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

Ссылки по теме

33
ответ дан 27 November 2019 в 03:46
поделиться

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

Это концепция полиморфизма .

11
ответ дан 27 November 2019 в 03:46
поделиться

Вы правы, но при необходимости вы можете сделать кастинг из интерфейса на нужного питомца. Например:

PetInterface p = new Cat();
((Cat)p).batheSelf();

Конечно, если вы попытаетесь привести питомца к собаке, вы не сможете вызвать метод batheSelf(). Он даже не скомпилируется. Поэтому, чтобы избежать проблем, вы могли бы иметь метод вроде этого:

public void bathe(PetInterface p){
    if (p instanceof Cat) {
        Cat c = (Cat) p;
        c.batheSelf();
    }
}

При использовании instanceof вы убеждаетесь, что не попытаетесь заставить собаку искупать себя во время выполнения. Что приведет к ошибке.

4
ответ дан 27 November 2019 в 03:46
поделиться

Да, вы правы. Если в Cat реализован «PetInterface», вы можете использовать его в приведенном выше примере и легко добавлять больше видов домашних животных. Если вам действительно нужно быть специфичным для Cat, вам необходимо получить доступ к классу Cat.

2
ответ дан 27 November 2019 в 03:46
поделиться

Вы можете вызвать метод batheSelf из talk в Cat.

2
ответ дан 27 November 2019 в 03:46
поделиться
Другие вопросы по тегам:

Похожие вопросы: