Чи є в інтерфейсі більше, ніж правильні методи


159

Отже, скажемо, що у мене є цей інтерфейс:

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();
}

Якщо це правда, то єдиною метою інтерфейсів є переконатися, що клас, який реалізує інтерфейс, має в ньому правильні методи, як описано інтерфейсом? Або є інше використання інтерфейсів?


2
Пам'ятайте, що інтерфейси не характерні лише для Java. Усі мови OOP мають їх у тій чи іншій формі, хоча не завжди так чітко визначені, як Java.
Гермс

2
Технічно всі сильно набрані мови OOP мають їх у тій чи іншій формі. Нетипізовані або набрані качками мови не мають подібного поняття.
Джаред

1
@Jred Хіба ви не плутаєте сильний набір тексту зі статичним набором тексту, а "нетипізований" з динамічно набраним?
eljenso

Поліморфізм може бути здійснений також через інтерфейси. Перевірте останній розділ цієї сторінки codenuggets.com/2014/06/20/java-interface
Джефф

Відповіді:


143

Інтерфейси - це спосіб зробити ваш код більш гнучким. Що ви робите, це:

Ibox myBox=new Rectangle();

Потім, пізніше, якщо ви вирішите використовувати інший вид вікна (можливо, є ще одна бібліотека з кращим видом поля), ви переключите свій код на:

Ibox myBox=new OtherKindOfBox();

Як тільки ви звикнете до нього, ви побачите, що це чудовий (насправді важливий) спосіб роботи.

Інша причина, наприклад, якщо ви хочете створити список скриньок і виконати певну операцію над кожною з них, але ви хочете, щоб список містив різні види коробок. У кожному вікні ви можете:

myBox.close()

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


54
У цій відповіді немає нічого, що виключно для інтерфейсів Java . Те саме стосується абстрактних класів, а то й конкретних. Я б очікував гарної відповіді на згадку про можливість реалізації декількох інтерфейсів, і коли / чому це було б корисно.
Rogério

16
Як це було обрано як відповідь? Це короткий опис того, чому поліморфізм корисний, але, як сказано в плакаті вище, я б очікував кращого пояснення декількох інтерфейсів і ще важливіше, коли доцільно використовувати інтерфейс проти абстрактного класу.
trevorkavanaugh

2
Це має дуже мало спільного з поясненням інтерфейсів та всього, що стосується основ поліморфізму
A-Developer-Has-No-Name

123

Що робить інтерфейси корисними, це не той факт, що "ви можете змінити свою думку і скористатися іншою реалізацією пізніше, і вам доведеться лише змінити те місце, де створений об'єкт". Це не питання.

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

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

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


9
Це досить гарна відповідь, мені подобається, коли ви наводили приклади з класів пакетів Java ...
Owais Qureshi

1
Мені сподобалосьyou can write code that operates on well-known interfaces, or interfaces you define
Маніш Кумар

Зачекайте мить ... Що робить інтерфейси корисними не [здатність використовувати будь-яку реалізацію, яка вам подобається], а скоріше [можливість використовувати будь-яку реалізацію, яка вам подобається]? Згадування протилежної справи є дуже хорошим моментом, хоча.
Powerslave

4
@Powerslave: перефразовуючи це більше як те, що робить інтерфейси корисними, це не [здатність писати код, коли потрібно змінювати лише один рядок під час зміни імптації], а скоріше [можливість писати код, де ви навіть не вказуєте реалізацію на всі].
Майкл Боргвардт

@MichaelBorgwardt Це звучить набагато краще. :) Дякую за уточнення!
Powerslave

119

Одне з численних застосунків, які я прочитав, - це те, що без Java-інтерфейсів, що використовують багатократне успадковування, на роботі 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 
} 

У тварини немає методу chew (), а замість цього розміщується в інтерфейсі як:

interface Chewable {
void chew();
}

і змусити клас Рептилій реалізувати це, а не Птахів (оскільки Птахи не можуть жувати):

class Reptile extends Animal implements Chewable { } 

а птахи - просто:

class Bird extends Animal { }

6
@CHEBURASHKA І погана назва. Якщо Reptile"жує", то не сам "жує". Конвенція про (іноді) іменування інтерфейсів Whateverable слід застосовувати лише там, де це має ідеальний сенс. PredatorТут найдоцільніше назвати інтерфейс .
Powerslave

6
@Powerslave Це правильно, рептилія "вміє жувати" / "жувати". Яструб - хижак, але все ще не в змозі жувати ... просто влучні, але "жувальні" можна визначити краще в документації інтерфейсу.
Мадменйо

Чудово .. Пояснив дуже добре. Дякую..!
Gurusinghe

1
Я не розумію. Здається, що інтерфейси - це хороший спосіб переконатися, що ви реалізуєте однакові методи у кожному класі, який реалізує його (наприклад, тому це заважає мені робити дурний вибір класу Bird з run () та класом Dog з runn () - вони всі однакові). Але, звернувши увагу і зробивши мої заняття однаковими форматами / структурами методів, я не зміг би досягти цього самого? Дійсно здається, що інтерфейси просто переконайтеся, що програміст не забудь. Крім того, інтерфейси, здається, не заощаджують мені час; Мені ще потрібно визначити метод у кожному класі, який його реалізує.
Алекс Г

@AlexG - я сказав одному з багатьох застосунків чувак :) Є ще багато, ми ледь не подряпали поверхню, щоб відповісти на питання простою мовою!
peevesy

47

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

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

Викликаючи scaleметод, ви можете надати будь-яке значення, яке має тип, який реалізує IBoxінтерфейс. Іншими словами, якщо Rectangleі те і Squareінше реалізувати IBox, ви можете надати або Rectangleабо, Squareде IBoxочікується.


8
Чому мета поліморфізму меж інтерфейсів, якщо я вже можу досягти цього на Яві з перекласифікацією підкласів та методів?
eljenso

1
Це те саме, за винятком того, що інтерфейси повинні опускати будь-яку реалізацію. Тому класи можуть реалізувати більше одного інтерфейсу.
Апокаліпс

4
Гей, я ніколи не говорив, що Java має якусь концептуальну цілісність. Заміна типу - мета всіх підтипів. У Java є більше одного механізму підтискання, жоден з яких не є особливо хорошим.
Апокаліпс

1
Я ніколи нічого не говорив про концептуальну цілісність. Але перейдемо далі. Якщо ви можете масштабувати кожен IBox за допомогою свого методу, чи не повинна це бути операція, оголошена на IBox: IBox.scale (int)?
eljenso

1
Ми не хотіли б зв'язувати Integer з IBox, тому ми не робимо це методом на Integer. А кількість методів на інтерфейсі визначається послідовністю та згуртованістю висловлених абстракцій, а не тим, наскільки громіздким було б його реалізувати. У будь-якому випадку, дякую за відповіді Апо.
eljenso

33

Інтерфейси дозволяють статично набраним мовам підтримувати поліморфізм. Пурист, орієнтований на об'єкти, наполягає на тому, що мова повинна забезпечувати успадкування, інкапсуляцію, модульність та поліморфізм, щоб бути повноцінною об'єктно-орієнтованою мовою. У мовах, що динамічно набираються - або качками (як Smalltalk), поліморфізм є тривіальним; однак у статично типових мовах (на зразок Java чи C #,) поліморфізм далеко не тривіальний (насправді на поверхні це, здається, суперечить поняттю сильного набору тексту).

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

У мові, що динамічно набирається (або набирається качка) (як Smalltalk), всі змінні є посиланнями на об'єкти (не менше і нічого більше.) Отже, у Smalltalk я можу це зробити:

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

anAnimal := Cow new.
anAnimal makeNoise.

Цей код:

  1. Заявляє локальну змінну під назвою anAnimal (зауважте, що ми НЕ вказуємо ТИП змінної - всі змінні є посиланнями на об'єкт, не більше і не менше.)
  2. Створює новий екземпляр класу під назвою "Pig"
  3. Призначає цей новий екземпляр Pig змінній anAnimal.
  4. Надсилає повідомлення makeNoiseсвині.
  5. Повторює всю справу, використовуючи корову, але привласнюючи її до тієї ж точної змінної, що і Свиня.

Той самий код Java виглядатиме приблизно так (припускаючи, що Дак і Корова є підкласами Animal:

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

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

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

У Smalltalk ми можемо написати це:

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

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

Це прекрасно працює в Smalltalk, тому що він набирає качку (якщо вона ходить як качка, і трясе як качка - це качка.) У цьому випадку, коли повідомлення надіслано об’єкту, здійснюється пошук на список методів приймача, і якщо знайдений метод відповідності, він викликається. Якщо ні, то викидається якийсь виняток NoSuchMethodError - але це все робиться під час виконання.

Але в Java, статично набраній мові, який тип можна віднести до нашої змінної? Кукурудзи потрібно успадковувати від Овочів, щоб підтримувати ріст, але не може успадковувати від Тварин, тому що не дає шуму. Корові потрібно успадковувати від тварини для підтримки makeNoise, але не може успадковувати її від 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. Інтерфейси дають нам багато переваг "справжнього" поліморфізму, без шкоди для перевірки типу компілятора.


2
Це текст моєї відповіді на інше питання: stackoverflow.com/questions/379282/… . Але, вони пов'язані відповіді.
Джаред

2
Тож я можу запитати, як мова, набрана качками, розрізняє Animal.water () (який, розсудливий фермер казав, що вона протікає), і Plant.water (), який він використовує для поливу рослин. Неоднозначність - ворог. ІМО прийнятна будь-яка кількість багатослівностей, необхідних для подолання неоднозначності.
Білл К

1
Так, двозначність - це назва гри з мовами, набраними качками. Працюючи професійно мовою, набраною качкою, не рідко бачити членів (методів та змінних) з іменами довжиною 50-100 символів.
Джаред

1
Ще одним важливим недоліком мов, набраних качкою, є неможливість зробити програмний рефакторинг на основі статичного аналізу - спробуйте задати зображення Smalltalk для списку всіх викликів вашого методу printString ... ви отримаєте список усіх викликів ВСІХ методів printString. ...
Джаред

... тому, що абонент Automobile # printString не може бути програмно відмежований від абонента NearEarthOrbit # printString.
Джаред

9

Зазвичай інтерфейси визначають інтерфейс, який слід використовувати (як випливає з назви ;-)). Зразок


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.


1
Чи повинен "Список" бути членом інтерфейсу?
Клацніть Upvote

1
Список - це інтерфейс у бібліотеці колекцій Java.
rmeador

Список - це інтерфейс у стандартній бібліотеці Java ( java.sun.com/javase/6/docs/api/java/util/List.html ). Він просто використовує це для ілюстрації своєї точки зору.
Майкл Майєрс

6

Я думаю, ви розумієте все, що робить Інтерфейс, але ви ще не уявляєте ситуацій, в яких Інтерфейс корисний.

Якщо ви створюєте інстанцію, використовуєте та випускаєте об'єкт все у вузькому діапазоні (наприклад, в рамках одного виклику методу), Інтерфейс насправді нічого не додає. Як ви зазначали, клас бетону відомий.

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

Тепер ви можете використовувати фабрику з різними методами createShape (params), які повертатимуть відповідну форму залежно від переданих парамерів. дбати про те, чи це коло, чи квадрат, і так далі.

Тепер уявіть, що у вас на різних фігурах вам належить виконати різні операції. Можливо, вам потрібно буде сортувати їх за площею, встановити їх на новий розмір, а потім відобразити в інтерфейсі. Фігури створені фабрикою, після чого їх можна легко передати класам сортування, шипера та дисплея. Якщо вам потрібно буде додати клас шестикутника деякий час у майбутньому, вам не доведеться нічого змінювати, крім заводських. Без інтерфейсу додавання іншої форми стає дуже безладним процесом.


6

ви могли б зробити

Ibox myBox = new Rectangle();

таким чином ви використовуєте цей об’єкт як Ibox, і вам не байдуже, що це насправді Rectangle.


Це означає, що ми могли написати так ?! > Прямокутник inst = новий Прямокутник ();
Dr.jacky

@ Mr.Hyde Якщо згодом ви хочете додати, у Squareвас виникнуть проблеми .... якщо ви спробуєте це зробити без інтерфейсів, ви не можете цього гарантувати Squareі Rectangleмати ті самі методи ... це може призвести до кошмару, коли у вас є більша база коду ... Пам'ятайте, що інтерфейси визначають шаблон.
Калонь Колоб

6

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

Починається з собаки. Зокрема мопс .

Мопс має різні форми поведінки:

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 } }

Але це явно не оптимально. Якщо я теж хочу розмістити деяких пуделів , я повинен змінити своє визначення Kennel, щоб додати масив пуделів. Насправді мені потрібен окремий масив для кожного виду собак.

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

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

Потім я трохи переробляю заняття Мопси та Лабораторії, щоб реалізувати поведінку Собаки. Можна сказати, що Мопс - собака, а лабораторія - собака.

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

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

Я ще можу інсталювати мопси та лабораторії, як раніше, але тепер я також отримую новий спосіб зробити це:

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

Інтерфейс дає вам змогу вказати набір поведінки, який спільно використовуватимуть усі класи, які реалізують інтерфейс. Отже, ми можемо визначити змінні та колекції (наприклад, масиви), які не повинні заздалегідь знати, який конкретний об’єкт вони матимуть, лише те, що вони будуть містити об’єкти, що реалізують інтерфейс.


@niranjan kurambhatti Я можу зробити всі класи для розширення собаки, але все ж чому інтерфейс ??
Jeeva

3

Чудовий приклад того, як використовуються інтерфейси, знаходиться в рамках колекцій. Якщо ви пишете функцію, яка займає функцію a List, це не має значення, переходить користувач у Vectorабо, ArrayListабо HashListчи, або інше. І ви можете передати , що Listдля будь-якої функції вимагаючи Collectionабо Iterableінтерфейс теж.

Це робить Collections.sort(List list)можливі функції , незалежно від того, як Listреалізовано.


3

Це причина, чому заводські шаблони та інші креативні моделі настільки популярні на Java. Ви впевнені, що без них Java не забезпечує механізм "поза коробкою" для легкої абстракції інстанцій. Тим не менш, ви отримуєте абстракцію скрізь, де не створюєте об'єкт у своєму методі, який повинен становити більшу частину вашого коду.

Як осторонь, я, як правило, закликаю людей не дотримуватися механізму "IRealname" для іменування інтерфейсів. Це річ Windows / COM, яка ставить одну ногу в могилу угорської нотації і справді не потрібна (Java вже сильно набрана, і вся справа в наявності інтерфейсів полягає в тому, щоб вони були якнайбільше відмінні від типів класів).


1
Ви плутаєте сильне введення тексту зі статичним набором тексту.
eljenso

3

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

Це стає трохи зрозуміліше , якщо інтерфейси називаються -able . напр

public interface Saveable {
....

public interface Printable {
....

і т.д. (схеми імен не завжди працюють, наприклад, я не впевнений Boxable, що тут доречно)


3

Єдина мета інтерфейсів - переконатися, що клас, який реалізує інтерфейс, має в ньому правильні методи, як описано інтерфейсом? Або є інше використання інтерфейсів?

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

З сторінки документації Oracle до підсумків інтерфейсу :

Декларація інтерфейсу може містити

  1. метод підписів
  2. методи за замовчуванням
  3. статичні методи
  4. постійні визначення.

Єдиними методами, які мають реалізацію, є методи за замовчуванням та статичні.

Використання інтерфейсу :

  1. Визначити договір
  2. Для зв’язку непов'язаних класів з можливостями (наприклад, реалізація класівSerializable інтерфейс може мати або не мати ніякого відношення між ними, крім реалізації цього інтерфейсу
  3. Забезпечити взаємозамінну реалізацію, наприклад, схему стратегії
  4. Методи за замовчуванням дозволяють додавати нові функціональні можливості в інтерфейси ваших бібліотек та забезпечувати бінарну сумісність з кодом, написаним для старих версій цих інтерфейсів.
  5. Організуйте допоміжні методи у своїх бібліотеках статичними методами (ви можете зберігати статичні методи, характерні для інтерфейсу в тому ж інтерфейсі, а не в окремому класі)

Деякі пов'язані питання SE щодо різниці між абстрактним класом та інтерфейсом та використання випадків із робочими прикладами:

Чим відрізняється інтерфейс від абстрактного класу?

Як я повинен пояснити різницю між інтерфейсом та абстрактним класом?

Перегляньте сторінку документації, щоб зрозуміти нові функції, додані в java 8: методи за замовчуванням та статичні методи .


Я видалив тег java-8, оскільки питання нічого не задавав про java-8 (і насправді його задавали задовго до java-8). Теги призначені для запитань, а не для відповідей.
Тагір Валєєв

2

Метою інтерфейсів є абстрагування або від'єднання від реалізації.

Якщо ви вводите абстракцію у свою програму, вас не хвилює можлива реалізація. Вас цікавить, що він може робити, а не як , і ви використовуєте, interfaceщоб виразити це на Java.


Мета всього структурованого програмування - абстрагування. Чому б ви сказали, що метою інтерфейсів є абстракція, оскільки я можу досягти точно такого ж, використовуючи загальну та класичну композицію?
Апокаліпс

1
Якщо все структуроване програмування є абстракцією (ваша заява), то інтерфейси - це абстракції в цій абстракції.
eljenso

1

Якщо у вас є CardboardBox та HtmlBox (обидва вони реалізують IBox), ви можете передати їх обох у будь-який метод, який приймає IBox. Незважаючи на те, що вони обидва дуже різні і не повністю взаємозамінні, методи, які не цікавляться "відкритими" або "змінити розміри", все одно можуть використовувати ваші класи (можливо, тому, що вони піклуються про те, скільки пікселів потрібно для відображення чогось на екрані).


1

Інтерфейси, де до 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(); 

Усі проблеми вирішуються за допомогою інтерфейсів, тому що ви знаєте, що ви можете розширити інтерфейси, і що у них звичайно є методи класифікації ... звичайно, компілятор хороший і каже вам, якщо ви не реалізували методи, але мені подобається думати, що це побічний ефект більш цікавої ідеї.


Можливо, ви хочете змінити між успадкуванням декількох реалізацій та успадкуванням кількох інтерфейсів у своїй відповіді, інакше це стає заплутаним.
eljenso

0

Ось моє розуміння переваги інтерфейсу. Виправте мене, якщо я помиляюся. Уявіть, що ми розробляємо ОС, а інша команда розробляє драйвери для деяких пристроїв. Отже, ми розробили інтерфейс 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...");    
    }
}

те ж саме можна зробити з абстрактними класами StorageDevice та FDD та HDD, що розширюють клас StorageDevice. але якщо ми використовуємо абстрактний клас, ми не можемо скористатися множинним успадкуванням.
Володимир Георгієв
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.