Есть ли в интерфейсе нечто большее, чем наличие правильных методов?

Допустим, у меня есть этот интерфейс:

public interface IBox
{
   public void setSize(int size);
   public int getSize();
   public int getArea();
  //...and so on
}

И у меня есть класс, который это реализует:

public class Rectangle implements IBox
{
   private int size;
   //Methods here
}

Если бы я хотел использовать интерфейс IBox, я не смог бы создать его экземпляр следующим образом:

public static void main(String args[])
{
    Ibox myBox=new Ibox();
}

Правильно? Так что мне действительно нужно было сделать это:

public static void main(String args[])
{
    Rectangle myBox=new Rectangle();
}

Если это правда, то единственная цель интерфейсов - убедиться, что класс, реализующий интерфейс, имеет в себе правильные методы, как описано в интерфейсе? Или есть еще какое-то использование интерфейсов?


person Ali    schedule 02.02.2009    source источник
comment
Помните, что интерфейсы не являются специфическими для Java. Они есть во всех языках ООП в той или иной форме, хотя и не всегда так явно, как в Java.   -  person Herms    schedule 03.02.2009
comment
Технически они в той или иной форме есть во всех строго типизированных языках ООП. Нетипизированные, или утино-типизированные языки не имеют подобной концепции.   -  person Jared    schedule 03.02.2009
comment
@Jared Разве вы не путаете строгую типизацию со статической типизацией, а нетипизированную - с динамически типизированной?   -  person eljenso    schedule 04.02.2009
comment
Полиморфизм также может быть выполнен через интерфейсы. Проверьте последний раздел этой страницы codenuggets.com/2014/06/20/java- интерфейс   -  person Jeff    schedule 01.07.2014
comment
stackoverflow.com/a/24436493/1286942   -  person Jo Smo    schedule 27.07.2015
comment
Что вы подразумеваете под правильными методами?   -  person Raedwald    schedule 21.06.2016


Ответы (17)


Интерфейсы - это способ сделать ваш код более гибким. Что вы делаете, так это:

Ibox myBox=new Rectangle();

Затем, позже, если вы решите, что хотите использовать другой тип ящика (возможно, есть другая библиотека с лучшим типом ящика), вы переключаете свой код на:

Ibox myBox=new OtherKindOfBox();

Когда вы к нему привыкнете, вы обнаружите, что это отличный (действительно необходимый) способ работы.

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

myBox.close()

(при условии, что у IBox есть метод close ()), хотя фактический класс myBox изменяется в зависимости от того, в каком поле вы находитесь на итерации.

person morgancodes    schedule 02.02.2009
comment
В этом ответе нет ничего эксклюзивного для интерфейсов Java. То же самое применимо и к абстрактным классам, или даже к конкретным. Я ожидал, что в хорошем ответе будет упоминание о возможности реализации нескольких интерфейсов и о том, когда / почему это будет полезно. - person Rogério; 14.10.2010
comment
Как это было выбрано в качестве ответа? Это краткое описание того, почему полиморфизм полезен, но, как сказано выше, я ожидал лучшего объяснения нескольких интерфейсов и, что еще более важно, когда уместно использовать интерфейс вместо абстрактного класса. - person trevorkavanaugh; 29.05.2014
comment
Это очень мало связано с объяснением интерфейсов и все, что связано с основами полиморфизма. - person A-Developer-Has-No-Name; 04.12.2016

Что делает интерфейсы полезными, так это не тот факт, что «вы можете передумать и использовать другую реализацию позже, и вам нужно будет изменить только одно место, где создается объект». Это не проблема.

Реальный момент уже в названии: они определяют интерфейс, который любой может реализовать для использования всего кода, который работает с этим интерфейсом. Лучшим примером является java.util.Collections, который предоставляет все виды полезных методов, которые работают исключительно с интерфейсами, например sort() или reverse() для List. Дело в том, что теперь этот код можно использовать для сортировки или реверсирования любого класса, реализующего List интерфейсы - не только ArrayList и LinkedList, но и классов, которые вы пишете сами, которые могут быть реализованы в пути люди, которые писали java.util.Collections, никогда не представляли.

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

Другое распространенное использование интерфейсов - обратные вызовы. Например, java.swing.table.TableCellRenderer, который позволяет вам влиять на то, как таблица Swing отображает данные в определенном столбце. Вы реализуете этот интерфейс, передаете экземпляр JTable, и в какой-то момент во время рендеринга таблицы ваш код будет вызван для выполнения своих задач.

person Michael Borgwardt    schedule 02.02.2009
comment
Это довольно хороший ответ, мне нравится, когда вы приводите примеры из классов пакетов java ... - person Owais Qureshi; 02.12.2013
comment
Мне понравился you can write code that operates on well-known interfaces, or interfaces you define - person Manish Kumar; 08.02.2014
comment
Подождите ... Что делает интерфейсы полезными, так это не [возможность использовать любую реализацию, которая вам нравится], а, скорее, [возможность использовать любую реализацию, которая вам нравится]? Однако упоминание противоположного случая в значительной степени хороший момент. - person Powerslave; 31.10.2014
comment
@Powerslave: перефразируйте это скорее как интерфейсы полезными делает не [возможность писать код, в котором вам нужно изменить только одну строку при изменении имплементации], а скорее [возможность писать код, в котором вы даже не указываете реализацию на все]. - person Michael Borgwardt; 31.10.2014
comment
@MichaelBorgwardt Это звучит лучше. :) Спасибо за разъяснение! - person Powerslave; 31.10.2014

Одно из многих применений, которые я прочитал, - это то, где сложно без интерфейсов с использованием множественного наследования в Java:

class Animal
{
void walk() { } 
....
.... //other methods and finally
void chew() { } //concentrate on this
} 

Теперь представьте себе случай, когда:

class Reptile extends Animal 
{ 
//reptile specific code here
} //not a problem here

но,

class Bird extends Animal
{
...... //other Bird specific code
} //now Birds cannot chew so this would a problem in the sense Bird classes can also call chew() method which is unwanted

Лучшим дизайном будет:

class Animal
{
void walk() { } 
....
.... //other methods 
} 

Animal не имеет метода chew () и вместо этого помещается в интерфейс как:

interface Chewable {
void chew();
}

и пусть класс Reptile реализует это, а не Birds (поскольку птицы не могут жевать):

class Reptile extends Animal implements Chewable { } 

а в случае с птицами просто:

class Bird extends Animal { }
person peevesy    schedule 20.06.2013
comment
@ ЧЕБУРАШКА И плохой нейминг. Если Reptile жует, то не разжевывается. Соглашение об именовании интерфейсов (иногда) Whateverable следует применять только там, где это имеет смысл. Здесь было бы более уместно назвать интерфейс Predator. - person Powerslave; 31.10.2014
comment
@Powerslave Это правильно имхо, рептилия умеет жевать / жевать. Ястреб - хищник, но он все еще не может жевать ... просто придирчивый, но жевательный, может быть лучше определен в документации интерфейса. - person Madmenyo; 12.11.2014
comment
Превосходно .. Очень хорошо объяснено. Спасибо..! - person Gurusinghe; 02.09.2015
comment
Я не понимаю. Кажется, что интерфейсы - это хороший способ убедиться, что вы реализуете одни и те же методы в каждом классе, который его реализует (например, поэтому он не позволяет мне сделать глупый выбор класса Bird с помощью run () и класса Dog с runn () - они все одинаковые). Но, обращая внимание и заставляя мои классы иметь одинаковые форматы / структуры методов, разве я не мог добиться того же? На самом деле кажется, что интерфейсы просто гарантируют, что программист не забывчивый. Кроме того, мне кажется, что интерфейсы не экономят время; Мне все еще нужно определить метод в каждом классе, который его реализует. - person Alex G; 28.04.2016
comment
@AlexG - я сказал, что один из многих использует, чувак :) Есть еще кое-что, мы едва поцарапали поверхность, чтобы ответить на вопрос простым способом! - person peevesy; 21.06.2016
comment
Но все же Chewable может быть классом, продолжающимся от Animal, а Bird расширяется от него. Было бы лучше, если бы вы могли дать chew() тело, чтобы все подклассы автоматически знали, как жевать по умолчанию, вместо того, чтобы повторять код, это не лучший пример, но все же ... - person BaSsGaz; 25.08.2017

Назначение интерфейсов - полиморфизм, также известный как подстановка типов. Например, учитывая следующий метод:

public void scale(IBox b, int i) {
   b.setSize(b.getSize() * i);
}

При вызове метода scale вы можете указать любое значение типа, реализующего интерфейс IBox. Другими словами, если Rectangle и Square реализуют IBox, вы можете указать либо Rectangle, либо Square везде, где ожидается IBox.

person Apocalisp    schedule 02.02.2009
comment
Зачем нужен полиморфизм интерфейсов, если я уже могу добиться этого в Java с помощью подклассов и переопределения методов? - person eljenso; 04.02.2009
comment
Это то же самое, за исключением того, что в интерфейсах не должно быть никакой реализации. Таким образом, классы могут реализовывать более одного интерфейса. - person Apocalisp; 04.02.2009
comment
Я спросил, потому что вы также можете сказать, что основной целью интерфейсов является абстракция (т. Е. Отделение от реализации), и, как и любой тип Java (будь он объявлен как класс или интерфейс), они допускают полиморфизм через наследование. - person eljenso; 04.02.2009
comment
Эй, я никогда не говорил, что Java обладает какой-либо концептуальной целостностью. Замена типа - цель всех подтипов. В Java есть несколько механизмов выделения подтипов, ни один из которых не особенно хорош. - person Apocalisp; 04.02.2009
comment
Я также никогда ничего не говорил о концептуальной целостности. Но идем дальше. Если вы можете масштабировать каждый IBox с помощью своего метода, разве это не должна быть операция, объявленная в IBox: IBox.scale (int)? - person eljenso; 04.02.2009
comment
Почему бы не сделать его методом на Integer? В любом случае для меня это не имеет значения, за исключением того, что чем больше методов вы добавляете в интерфейс, тем труднее его реализовать. - person Apocalisp; 04.02.2009
comment
Мы не хотели бы связывать Integer с IBox, поэтому мы не делаем его методом на Integer. И количество методов в интерфейсе определяется согласованностью и связностью абстракции, которую он выражает, а не тем, насколько громоздкой будет ее реализация. В любом случае, спасибо за ответы, Апо. - person eljenso; 05.02.2009

Интерфейсы позволяют статически типизированным языкам поддерживать полиморфизм. Пурист с объектно-ориентированной ориентацией будет настаивать на том, что язык должен обеспечивать наследование, инкапсуляцию, модульность и полиморфизм, чтобы быть полнофункциональным объектно-ориентированным языком. В языках с динамической типизацией (например, в Smalltalk) полиморфизм тривиален; однако в статически типизированных языках (таких как Java или C #) полиморфизм далеко не тривиален (фактически, на первый взгляд кажется, что он противоречит понятию строгой типизации).

Позвольте мне продемонстрировать:

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

|anAnimal|    
anAnimal := Pig new.
anAnimal makeNoise.

anAnimal := Cow new.
anAnimal makeNoise.

Этот код:

  1. Объявляет локальную переменную с именем anAnimal (обратите внимание, что мы НЕ указываем ТИП переменной - все переменные являются ссылками на объект, не больше и не меньше.)
  2. Создает новый экземпляр класса с именем «Свинья».
  3. Назначает этот новый экземпляр Pig переменной anAnimal.
  4. Отправляет сообщение makeNoise свинье.
  5. Повторяет все, используя корову, но присваивая ей ту же точную переменную, что и Pig.

Тот же код Java будет выглядеть примерно так (если предположить, что Duck и Cow являются подклассами Animal:

Animal anAnimal = new Pig();
duck.makeNoise();

anAnimal = new Cow();
cow.makeNoise();

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

В Smalltalk мы можем написать так:

|aFarmObject|
aFarmObject := Cow new.
aFarmObject grow.
aFarmObject makeNoise.

aFarmObject := Corn new.
aFarmObject grow.
aFarmObject harvest.

Это прекрасно работает в Smalltalk, потому что он типизирован как утка (если он ходит как утка и крякает как утка - это утка). В этом случае, когда сообщение отправляется объекту, поиск выполняется по список методов получателя, и если соответствующий метод найден, он вызывается. Если нет, генерируется какое-то исключение NoSuchMethodError, но все это делается во время выполнения.

Но в Java, статически типизированном языке, какой тип мы можем присвоить нашей переменной? Кукуруза должна наследовать от Vegetable, чтобы поддерживать рост, но не может наследовать от Animal, потому что она не создает шума. Cow должна наследовать от Animal для поддержки makeNoise, но не может наследовать от Vegetable, потому что она не должна реализовывать сбор урожая. Похоже, нам нужно множественное наследование - возможность наследовать более чем от одного класса. Но это оказывается довольно сложной функцией языка из-за всех всплывающих краевых случаев (что происходит, когда несколько параллельных суперклассов реализуют один и тот же метод? И т. Д.)

А вот и интерфейсы ...

Если мы создадим классы Animal и Vegetable, каждый из которых реализует Growable, мы сможем объявить, что наша Cow - Animal, а наша Corn - Vegetable. Мы также можем заявить, что и животные, и овощи пригодны для выращивания. Это позволяет нам написать это, чтобы вырастить все:

List<Growable> list = new ArrayList<Growable>();
list.add(new Cow());
list.add(new Corn());
list.add(new Pig());

for(Growable g : list) {
   g.grow();
}

И это позволяет нам издавать звуки животных:

List<Animal> list = new ArrayList<Animal>();
list.add(new Cow());
list.add(new Pig());
for(Animal a : list) {
  a.makeNoise();
}

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

|aFarmObject|
aFarmObject := Corn new.
aFarmObject makeNoise. // No compiler error - not checked until runtime.

Языки со статической типизацией обеспечивают гораздо лучшее «программирование по контракту», потому что во время компиляции они улавливают два вида ошибок, указанных ниже:

// Compiler error: Corn cannot be cast to Animal.
Animal farmObject = new Corn();  
farmObject makeNoise();

--

// Compiler error: Animal doesn't have the harvest message.
Animal farmObject = new Cow();
farmObject.harvest(); 

Итак .... резюмируем:

  1. Реализация интерфейса позволяет вам указать, какие действия могут выполнять объекты (взаимодействие), а наследование классов позволяет вам указать, как должны выполняться действия (реализация).

  2. Интерфейсы дают нам много преимуществ «настоящего» полиморфизма без ущерба для проверки типов компилятором.

person Jared    schedule 02.02.2009
comment
Это текст моего ответа на другой вопрос: stackoverflow.com/questions/379282/. Но это связанные ответы. - person Jared; 03.02.2009
comment
Итак, позвольте мне спросить, как язык с утиным типом различает Animal.water () (который ханжеский фермер говорил, что он берет утечку) и Plant.water (), который он использует для полива растений. Двусмысленность - враг. ИМО, допустима любая многословность, необходимая для преодоления двусмысленности. - person Bill K; 03.02.2009
comment
Ага .. неоднозначность - это название игры с утиными языками. При профессиональной работе с языком с утиным типом нередко можно увидеть элементы (методы и переменные) с именами длиной от 50 до 100 символов. - person Jared; 03.02.2009
comment
Еще одним большим недостатком языков с утиным типом является невозможность выполнить программный рефакторинг на основе статического анализа - попробуйте запросить образ Smalltalk для получения списка всех вызывающих ваш метод printString ... вы получите список всех вызывающих ВСЕ методы printString. ... - person Jared; 03.02.2009
comment
... потому что вызывающий объект Automobile # printString нельзя программно отличить от вызывающего объекта NearEarthOrbit # printString. - person Jared; 03.02.2009
comment
Думаю, я рад, что существуют оба варианта. Мне нравится надежность нанесения интерфейса на то, что можно сказать «ЭТО УТКА», но я вижу, насколько это совершенно ненужно (и даже вредно) для небольших проектов с небольшими командами. - person Bill K; 03.02.2009
comment
Вы путаете строго типизированный со статически типизированным и слабо типизированный с динамически типизированным. - person eljenso; 04.02.2009
comment
Согласно Википедии, термины строго типизированный и слабо типизированный очень универсальны - и использовались таким образом в прошлом. Тем не мение; статически типизированные и динамически типизированные намного точнее. Я отредактировал ответ, чтобы использовать вместо этого эту терминологию. Спасибо. - person Jared; 04.02.2009
comment
Ты должен был заставить свинью летать ... намного веселее. - person Edward J Beckett; 01.03.2015

Обычно интерфейсы определяют интерфейс, который вы должны использовать (как следует из названия ;-)). Образец


public void foo(List l) {
   ... do something
}

Теперь ваша функция foo принимает ArrayLists, LinkedLists, ... не только один тип.

Самое главное в Java - это то, что вы можете реализовать несколько интерфейсов, но вы можете расширить только ОДИН класс! Пример:


class Test extends Foo implements Comparable, Serializable, Formattable {
...
}
возможно, но

class Test extends Foo, Bar, Buz {
...
}
- нет!

Ваш код выше также может быть: IBox myBox = new Rectangle();. Теперь важно то, что myBox содержит ТОЛЬКО методы / поля из IBox, а не (возможно, существующие) другие методы из Rectangle.

person Johannes Weiss    schedule 02.02.2009
comment
"Список" должен быть членом интерфейса? - person Ali; 03.02.2009
comment
Список - это интерфейс в библиотеке коллекций java. - person rmeador; 03.02.2009
comment
Список - это интерфейс стандартной библиотеки Java (java. sun.com/javase/6/docs/api/java/util/List.html). Он просто использует это, чтобы проиллюстрировать свою точку зрения. - person Michael Myers; 03.02.2009

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

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

Интерфейсы полезны, когда объект нужно создать в одном месте и вернуть вызывающей стороне, которая может не заботиться о деталях реализации. Давайте изменим ваш пример IBox на Shape. Теперь у нас могут быть реализации Shape, такие как Rectangle, Circle, Triangle и т. Д. Реализации методов getArea () и getSize () будут совершенно разными для каждого конкретного класса.

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

А теперь представьте, что вам нужно выполнить множество операций с фигурами. Возможно, вам нужно отсортировать их по площади, установить для всех новый размер, а затем отобразить их в пользовательском интерфейсе. Все формы создаются фабрикой, а затем могут быть очень легко переданы классам Sorter, Sizer и Display. Если вам когда-нибудь понадобится добавить класс шестиугольника, вам не нужно ничего менять, кроме фабрики. Без интерфейса добавление еще одной формы становится очень запутанным процессом.

person Ickster    schedule 02.02.2009

ты мог бы сделать

Ibox myBox = new Rectangle();

таким образом вы используете этот объект как Ibox, и вам все равно, что это действительно Rectangle.

person IAdapter    schedule 02.02.2009
comment
Значит, мы могли так писать ?! ›Прямоугольник inst = новый Прямоугольник (); - person Dr.jacky; 17.04.2016
comment
@ Mr.Hyde. Если вы позже захотите добавить Square, у вас возникнут проблемы .... если вы попытаетесь сделать это без интерфейсов, вы не можете гарантировать, что Square и Rectangle имеют одинаковые методы ... это может привести к кошмару когда у вас большая база кода ... Помните, интерфейсы определяют шаблон. - person Kellen Stuart; 06.06.2018

ПОЧЕМУ ИНТЕРФЕЙС ??????

Все начинается с собаки. В частности, мопс.

Мопс ведет себя по-разному:

public class Pug { 
private String name;
public Pug(String n) { name = n; } 
public String getName() { return name; }  
public String bark() { return  "Arf!"; } 
public boolean hasCurlyTail() { return true; } }

И у вас есть лабрадор, у которого тоже есть набор поведения.

public class Lab { 
private String name; 
public Lab(String n) { name = n; } 
public String getName() { return name; } 
public String bark() { return "Woof!"; } 
public boolean hasCurlyTail() { return false; } }

Мы можем сделать несколько мопсов и лабрадоров:

Pug pug = new Pug("Spot"); 
Lab lab = new Lab("Fido");

И мы можем вызвать их поведение:

pug.bark() -> "Arf!" 
lab.bark() -> "Woof!" 
pug.hasCurlyTail() -> true 
lab.hasCurlyTail() -> false 
pug.getName() -> "Spot"

Допустим, у меня собачий питомник, и мне нужно отслеживать всех собак, которых я держу. Мне нужно хранить своих мопсов и лабрадоров в отдельных массивах:

public class Kennel { 
Pug[] pugs = new Pug[10]; 
Lab[] labs = new Lab[10];  
public void addPug(Pug p) { ... } 
public void addLab(Lab l) { ... } 
public void printDogs() { // Display names of all the dogs } }

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

Понимание: и мопсы, и лабрадоры (и пудели) относятся к типам собак, и у них одинаковый набор поведения. То есть мы можем сказать (для целей этого примера), что все собаки могут лаять, иметь имя и могут иметь или не иметь вьющийся хвост. Мы можем использовать интерфейс для определения того, что могут делать все собаки, но оставим на усмотрение конкретных типов собак реализовать это конкретное поведение. Интерфейс говорит: «Вот вещи, которые могут делать все собаки», но не говорит, как выполняется каждое поведение.

public interface Dog 
{
public String bark(); 
public String getName(); 
public boolean hasCurlyTail(); }

Затем я немного изменяю классы Pug и Lab, чтобы реализовать поведение Dog. Можно сказать, что мопс - это собака, а Лабрадор - это собака.

public class Pug implements Dog {
// the rest is the same as before } 

public class Lab implements Dog { 
// the rest is the same as before 
}

Я все еще могу создавать экземпляры Pugs and Labs, как раньше, но теперь у меня есть новый способ сделать это:

Dog d1 = new Pug("Spot"); 
Dog d2 = new Lab("Fido");

Это говорит о том, что d1 - это не только собака, это конкретно мопс. И d2 тоже собака, а точнее лаборатория. Мы можем вызывать поведения, и они работают как раньше:

d1.bark() -> "Arf!" 
d2.bark() -> "Woof!" 
d1.hasCurlyTail() -> true 
d2.hasCurlyTail() -> false 
d1.getName() -> "Spot"

Вот где окупается вся дополнительная работа. Класс питомника стал намного проще. Мне нужен только один массив и один метод addDog. Оба будут работать с любым объектом, которым является собака; то есть объекты, реализующие интерфейс Dog.

public class Kennel {
Dog[] dogs = new Dog[20]; 
public void addDog(Dog d) { ... } 
public void printDogs() {
// Display names of all the dogs } }

Вот как это использовать:

Kennel k = new Kennel(); 
Dog d1 = new Pug("Spot"); 
Dog d2 = new Lab("Fido"); 
k.addDog(d1); 
k.addDog(d2); 
k.printDogs();

Последнее утверждение отобразит: Spot Fido

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

person niranjan kurambhatti    schedule 15.05.2017
comment
@niranjan kurambhatti я могу сделать все классы для расширения dog, но все же почему интерфейс ?? - person Jeeva; 09.05.2019

Отличный пример использования интерфейсов - фреймворк Collections. Если вы пишете функцию, которая принимает List, тогда не имеет значения, передает ли пользователь Vector, ArrayList или HashList или что-то еще. И вы можете передать это List любой функции, требующей интерфейса Collection или Iterable.

Это делает возможными такие функции, как Collections.sort(List list), независимо от того, как реализован List.

person Kip    schedule 02.02.2009

Это причина того, почему Factory Patterns и другие шаблоны создания так популярны в Java. Вы правы, что без них Java не предоставляет готового механизма для простой абстракции создания экземпляров. Тем не менее, вы получаете абстракцию везде, где вы не создаете объект в своем методе, который должен составлять большую часть вашего кода.

Кстати, я обычно рекомендую людям не использовать механизм IRealname для именования интерфейсов. Это вещь Windows / COM, которая ставит одну ногу в могилу венгерской нотации и в действительности не является необходимой (Java уже строго типизирована, и весь смысл наличия интерфейсов в том, чтобы они были настолько неотличимы от типов классов, насколько это возможно).

person Christopher Smith    schedule 02.02.2009
comment
Вы путаете строгую типизацию со статической типизацией. - person eljenso; 04.02.2009

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

Это станет немного понятнее, если интерфейсы будут названы -able. например

public interface Saveable {
....

public interface Printable {
....

и т. д. (схемы именования не всегда работают, например, я не уверен, что Boxable здесь подходит)

person Brian Agnew    schedule 03.02.2009

единственная цель интерфейсов - убедиться, что класс, реализующий интерфейс, имеет в себе правильные методы, как описано в интерфейсе? Или есть еще какое-то использование интерфейсов?

Я обновляю ответ новыми функциями интерфейса, которые появились в версии java 8.

Со страницы документации Oracle на кратком описании интерфейса:

Объявление интерфейса может содержать

  1. сигнатуры методов
  2. методы по умолчанию
  3. статические методы
  4. постоянные определения.

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

Использование интерфейса:

  1. Чтобы определить контракт
  2. Чтобы связать несвязанные классы с имеет возможности (например, классы, реализующие интерфейс Serializable, могут иметь или не иметь между собой никаких отношений, кроме реализации этого интерфейса
  3. Для обеспечения взаимозаменяемой реализации например шаблон стратегии
  4. Методы по умолчанию позволяют добавлять новые функции в интерфейсы ваших библиотек и обеспечивать двоичную совместимость с кодом, написанным для более старых версий этих интерфейсов.
  5. Организуйте вспомогательные методы в своих библиотеках с помощью статических методов (вы можете сохранить статические методы, специфичные для интерфейса, в том же интерфейсе, а не в отдельном классе)

Некоторые связанные вопросы SE относительно разницы между абстрактным классом и интерфейсом и варианты использования с рабочими примерами:

В чем разница между интерфейсом и абстрактный класс?

Как следует Я объяснил разницу между интерфейсом и абстрактным классом?

Чтобы узнать о новых функциях, добавленных в java 8: методы по умолчанию и статические методы.

person Ravindra babu    schedule 04.03.2016
comment
Я удалил тег java-8, поскольку question ничего не спрашивал о java-8 (и на самом деле его задавали задолго до java-8). Теги предназначены для вопросов, а не для ответов. - person Tagir Valeev; 07.03.2016

Назначение интерфейсов - абстракция или отделение от реализации.

Если вы вводите абстракцию в свою программу, вас не волнуют возможные реализации. Вас интересует что он может делать, а не как, и вы используете interface, чтобы выразить это на Java.

person eljenso    schedule 04.02.2009
comment
Цель всего структурированного программирования - абстракция. Почему вы говорите, что цель интерфейсов - абстракция, если я могу добиться того же, используя обобщения и композицию классов? - person Apocalisp; 04.02.2009
comment
Если все структурированное программирование является абстракцией (ваше утверждение), то интерфейсы являются абстракциями в этой абстракции. - person eljenso; 04.02.2009

Если у вас есть CardboardBox и HtmlBox (оба реализуют IBox), вы можете передать их оба в любой метод, принимающий IBox. Несмотря на то, что они оба очень разные и не полностью взаимозаменяемы, методы, которые не заботятся об «открытии» или «изменении размера», все равно могут использовать ваши классы (возможно, потому, что они заботятся о том, сколько пикселей необходимо для отображения чего-либо на экране).

person Todd R    schedule 02.02.2009

Интерфейсы, в которых в java добавлен элемент, позволяющий множественное наследование. Однако разработчики Java осознали / осознали, что множественное наследование - это «опасная» функция, поэтому они и пришли к идее интерфейса.

множественное наследование опасно, потому что у вас может быть такой класс:


class Box{
    public int getSize(){
       return 0;
    }
    public int getArea(){
       return 1;
    }

}

class Triangle{
    public int getSize(){
       return 1;
    }
    public int getArea(){
       return 0;
    }

}

class FunckyFigure extends Box, Triable{
   // we do not implement the methods we will used the inherited ones
}

Какой метод следует вызывать, когда мы используем


   FunckyFigure.GetArea(); 

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

person mandel    schedule 02.02.2009
comment
Возможно, вы захотите сделать различие между наследованием множественных реализаций и множественным наследованием интерфейсов в своем ответе, иначе это запутается. - person eljenso; 04.02.2009

Вот мое понимание преимуществ интерфейса. Поправьте меня, если я ошибаюсь. Представьте, что мы разрабатываем ОС, а другая команда разрабатывает драйверы для некоторых устройств. Итак, мы разработали интерфейс StorageDevice. У нас есть две его реализации (FDD и HDD), предоставленные другой командой разработчиков.

Затем у нас есть класс OperatingSystem, который может вызывать методы интерфейса, такие как saveData, просто передавая экземпляр класса, реализующего интерфейс StorageDevice.

Преимущество здесь в том, что нас не волнует реализация интерфейса. Другая команда выполнит эту работу, реализовав интерфейс StorageDevice.

package mypack;

interface StorageDevice {
    void saveData (String data);
}


class FDD implements StorageDevice {
    public void saveData (String data) {
        System.out.println("Save to floppy drive! Data: "+data);
    }
}

class HDD implements StorageDevice {
    public void saveData (String data) {
        System.out.println("Save to hard disk drive! Data: "+data);
    }
}

class OperatingSystem {
    public String name;
    StorageDevice[] devices;
    public OperatingSystem(String name, StorageDevice[] devices) {

        this.name = name;
        this.devices = devices.clone();

        System.out.println("Running OS " + this.name);
        System.out.println("List with storage devices available:");
        for (StorageDevice s: devices) {
            System.out.println(s);
        }

    }

    public void saveSomeDataToStorageDevice (StorageDevice storage, String data) {
        storage.saveData(data);
    }
}

public class Main {

    public static void main(String[] args) {

        StorageDevice fdd0 = new FDD();
        StorageDevice hdd0 = new HDD();     
        StorageDevice[] devs = {fdd0, hdd0};        
        OperatingSystem os = new OperatingSystem("Linux", devs);
        os.saveSomeDataToStorageDevice(fdd0, "blah, blah, blah...");    
    }
}
person Vladimir Georgiev    schedule 22.06.2018
comment
то же самое можно сделать с абстрактным классом StorageDevice и классами FDD и HDD, расширяющими класс StorageDevice. но если мы используем абстрактный класс, мы не сможем воспользоваться преимуществами множественного наследования. - person Vladimir Georgiev; 22.06.2018