Зачем вам объявлять интерфейс, а затем создавать экземпляр объекта с ним в Java? - PullRequest
21 голосов
/ 10 октября 2011

Мы с другом изучаем Java. Сегодня мы рассматривали интерфейсы и немного поговорили об использовании интерфейсов.

Пример кода, который мой друг показал мне, содержал это:

IVehicle modeOfTransport1 = new Car();
IVehicle modeOfTransport2 = new Bike();

Где IVehicle - это интерфейс, который реализован в классах автомобилей и велосипедов. При определении метода, который принимает IVehicle в качестве параметра, вы можете использовать методы интерфейса, а при запуске кода вышеуказанные объекты работают как обычно. Тем не менее, это прекрасно работает при объявлении автомобиля и велосипеда, как вы обычно хотели бы это:

Car modeOfTransport1 = new Car();
Bike modeOfTransport2 = new Bike();

Итак, мой вопрос - зачем использовать первый метод поверх второго при объявлении и создании экземпляров объектов modeOfTransport ? Имеет ли это значение?

Ответы [ 10 ]

13 голосов
/ 10 октября 2011

Существует большой плюс при объявлении их с использованием интерфейса, который известен как «кодирование в интерфейс» вместо «кодирование в реализацию» , который является big Object-Oriented Design (OOD) принцип, таким образом, вы можете объявить метод следующим образом:

public void (IVehicle myVehicle)

, и это примет любой объект, который реализует этот интерфейс, тогда во время выполнения это вызовет реализацию как это:

public void (IVehicle myVehicle)
{
    myVehicle.run() //This calls the implementation for that particular vehicle.
}

Чтобы ответить на исходный вопрос, почему вы используете один над другим, есть несколько причин:

1) Объявление их с помощью интерфейса означает, что вы можете позже заменить это значение любым другим конкретным классом, реализующим этот интерфейс, вместо того, чтобы быть заблокированным в этом конкретном классе

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

3) Вы придерживаетесь принципа кода OOD для интерфейса

9 голосов
/ 10 октября 2011

Это не имеет значения.

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

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

Если у вас был последующий код, в котором использовались определенные операции для автомобилей или мотоциклов, было бы целесообразно объявить их как автомобили или мотоциклы.Для каждого из соответствующих объектов будут доступны специальные операции для автомобиля и велосипеда, и оба будут использоваться (т.е. могут передаваться) как IVehicle.

5 голосов
/ 10 октября 2011

Вы действительно спрашиваете: какой тип ссылки я должен использовать?

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

Рассмотрите следующие варианты:

Set<String> values1 = new TreeSet<String>();
TreeSet<String> values2 = new TreeSet<String>();
SortedSet<String> values3 = new TreeSet<String>();

Все три действительны, но, как правило, первый вариант values1 лучше, потому что вы сможете получить доступ только к поведению интерфейса Set, так что позже вы сможете легко заменить другую реализацию:

Set<String> values1 = new HashSet<String>();

Остерегайтесь использования второй опции values2.Это позволяет вам использовать конкретное поведение реализации TreeSet таким образом, что замена в другой реализации Set становится более сложной.Это хорошо, если это ваша цель.Итак, в вашем примере используйте ссылку Car или Bike только тогда, когда вам нужен доступ к тому, чего нет в интерфейсе IVehicle.Имейте в виду, что следующее не сработает:

TreeSet<String> values2 = new HashSet<String>(); // does not compile!

Тем не менее бывают случаи, когда вам нужен доступ к методам, которые не относятся к наиболее общему типу.Это проиллюстрировано в третьем варианте values3 - ссылка более конкретна, чем Set, что позволяет вам полагаться на поведение SortedSet позже.

TreeSet<String> values3 = new ConcurrentSkipListSet<String>();

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

3 голосов
/ 10 октября 2011

Программа для интерфейса, а не для реализации.

Когда вы программируете интерфейс, вы пишете код, который может обрабатывать любой тип Транспортного средства. Поэтому в будущем ваш код без изменений должен работать с поездами и самолетами.

Если вы игнорируете интерфейс, значит, вы застряли с CArs и Bikes, и любые новые Транспортные средства потребуют дополнительных модификаций кода.

Принцип, лежащий в основе этого:

Открыт для расширения, закрыт для модификации.

3 голосов
/ 10 октября 2011

Потому что вас не волнует, что такое реализация ... только каково ее поведение.

Скажем, у вас есть животное

interface Animal {
    String speak();
}

class Cat implements Animal {

    void claw(Furniture f) { /* code here */ }

    public String speak() { return "Meow!" }
}

class Dog implements Animal {

    void water(FireHydrant fh) { /* code here */ }

    public String speak() { return "Woof!"; }
}

Теперь вы хотите, чтобы ваш ребенок получил домашнее животное.

Animal pet = new ...?
kid.give(pet);

А ты вернешь его позже

Animal pet = kid.getAnimal();

Вы не хотели бы идти

pet.claw(favorateChair);

Потому что вы не знаете, есть ли у ребенка собака или нет. И тебе все равно. Вы только знаете, что животным разрешено говорить. Вы ничего не знаете об их взаимодействии с мебелью или пожарными гидрантами. Вы знаете, животные для разговора. И это заставляет вашу дочь хихикать (или нет!)

kid.react(pet.speak());

При этом, когда вы делаете золотую рыбку, реакция ребенка довольно хромая (оказывается, золотые рыбки не говорят!) Но когда вы кладете медведя, реакция довольно страшная!

И ты не смог бы сделать это, если бы сказал

Cat cat = new Cat();

потому что вы ограничиваете себя способностями Кошки.

2 голосов
/ 10 октября 2011

Честно говоря, ваш аргумент довольно спорный.Здесь происходит неявное преобразование в IVehicle.Вы и ваш друг, кажется, спорите о том, лучше ли это делать немедленно (согласно первому листингу кода) или позже (когда вы вызываете метод, согласно второму листингу кода).В любом случае, он будет неявно преобразован в IVehicle, поэтому реальный вопрос - вам нужно иметь дело с автомобилем или просто транспортным средством?Если все, что вам нужно, это IVehicle, то первый способ вполне подойдет (и предпочтительнее, если на более позднем этапе вы захотите заменить автомобиль на велосипед).Если вам нужно относиться к нему как к машине в других точках вашего кода, просто оставьте это как машину.

1 голос
/ 10 октября 2011

хорошо интерфейсы - это поведения, а классы - их реализация, поэтому будет несколько случаев, когда вы будете программировать, где будете знать только поведение (интерфейс). и чтобы использовать это, вы будете применять их, чтобы извлечь из этого пользу. в основном он используется для сокрытия деталей реализации от пользователя, только сообщая им поведение (интерфейс).

1 голос
/ 10 октября 2011

Объявление интерфейсов и создание их экземпляров с объектами допускает мощную концепцию, называемую полиморфизм .

List<IVehicle> list = new ArrayList<IVehicle>();
list.add(new Car());
list.add(new Bike());

for (int i = 0; i < list.size(); ++i)
    list.get(i).doSomeVehicleAction();   // declared in IVehicle and implemented differently in Car and Bike

Чтобы явным образом ответить на вопрос: вы должны использовать объявление интерфейса (даже если вы знаете конкретный тип), чтобы вы могли передавать несколько типов (которые реализуют один и тот же интерфейс) в метод или коллекцию; тогда поведение, общее для каждого реализующего типа, может быть вызвано независимо от того, что является фактическим типом.

0 голосов
/ 13 сентября 2013

При "IVehicle modeOfTransport1 = new Car ();" методы, принадлежащие только Car, недоступны для modeOfTransport1.Я все равно не знаю причину.

0 голосов
/ 11 октября 2011

Ваша интуиция верна;тип переменной должен быть как можно более конкретным.

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

Переменные не являются частью API.Это детали реализации.Абстракция обычно не применяется.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...