Я читаю "Підручник з Java" (вдруге). Я щойно пройшов через розділ Інтерфейси (знову ж таки), але досі не розумію, як Інтерфейси Java імітують множинне успадкування. Чи є більш чітке пояснення, ніж те, що є в книзі?
Я читаю "Підручник з Java" (вдруге). Я щойно пройшов через розділ Інтерфейси (знову ж таки), але досі не розумію, як Інтерфейси Java імітують множинне успадкування. Чи є більш чітке пояснення, ніж те, що є в книзі?
Відповіді:
Припустимо, у вас є 2 види речей у вашому домені: вантажівки та кухні
Вантажівки мають метод driveTo (), а Кухні - кухаря ().
Тепер припустимо, Паулі вирішив продати піцу ззаду вантажівки для доставки. Він хоче щось, де він може водити машину () та готувати їжу ().
У C ++ він використовував би багаторазове успадкування для цього.
У Java, яка вважалася занадто небезпечною, тому ви можете успадкувати основний клас, але можете "успадкувати" поведінку від інтерфейсів, які є для всіх намірів абстрактними класами без полів чи реалізацій методів.
Отже, в Java ми схильні реалізовувати множинне успадкування за допомогою делегувань:
Паулі підкласує вантажівку та додає кухню до вантажівки у змінну члена, яка називається кухня. Він реалізує інтерфейс Kitchen, викликаючи kitchen.cook ().
class PizzaTruck extends Truck implements Kitchen {
Kitchen kitchen;
public void cook(Food foodItem) {
kitchen.cook(foodItem);
}
}
Він щаслива людина, бо тепер він може робити такі речі;
pizzaTruck.driveTo(beach);
pizzaTruck.cook(pizzaWithExtraAnchovies);
Гаразд, ця безглузда історія мала сказати, що це не імітація багаторазового успадкування, це реальне багаторазове успадкування з умовою, що ви можете успадкувати лише контракт, лише успадкувати від порожніх абстрактних базових класів, які називаються інтерфейсами.
(оновлення: з появою методів за замовчуванням інтерфейси тепер також можуть забезпечити деяку поведінку, яка успадковується)
public void cook()
), який би реалізовували і Kitchen, і PizzaTruck.
default
методам. Інтерфейси по- , як і раніше не мають стану екземпляра, але вони роблять є методи клас може успадкувати.
Вас, мабуть, бентежить, оскільки ви переглядаєте множинне успадкування локально, з точки зору того, що один клас успадковує деталі реалізації від кількох батьків. Це неможливо на Java (і часто призводить до зловживань на мовах, де це можливо).
Інтерфейси дозволяють багаторазове успадкування типів , наприклад a class Waterfowl extends Bird implements Swimmer
може використовуватися іншими класами, як якщо б це булоBird
і ніби це a Swimmer
. У цьому полягає глибший сенс багаторазового успадкування: дозволяти одному об’єкту діяти так, ніби він належить одночасно кільком не пов’язаним між собою різним класам.
Ось спосіб досягти багаторазового успадкування через інтерфейси в Java.
Чого досягти?
клас A поширюється на B, C // це неможливо безпосередньо в Java, але може бути досягнуто опосередковано.
class B{
public void getValueB(){}
}
class C{
public void getValueC(){}
}
interface cInterface{
public getValueC();
}
class cChild extends C implemets cInterface{
public getValueC(){
// implementation goes here, call the super class's getValueC();
}
}
// Below code is **like** class A extends B, C
class A extends B implements cInterface{
cInterface child = new cChild();
child.getValueC();
}
враховуючи два інтерфейси нижче ...
interface I1 {
abstract void test(int i);
}
interface I2 {
abstract void test(String s);
}
Ми можемо реалізувати обидва з них, використовуючи код нижче ...
public class MultInterfaces implements I1, I2 {
public void test(int i) {
System.out.println("In MultInterfaces.I1.test");
}
public void test(String s) {
System.out.println("In MultInterfaces.I2.test");
}
public static void main(String[] a) {
MultInterfaces t = new MultInterfaces();
t.test(42);
t.test("Hello");
}
}
Ми НЕ МОЖЕМО розширити два об’єкти, але ми можемо реалізувати два інтерфейси.
Інтерфейси не імітують множинне успадкування. Творці Java вважали багаторазове успадкування помилковим, тому в Java такого немає.
Якщо ви хочете поєднати функціональність двох класів в один - використовуйте композицію об’єкта. Тобто
public class Main {
private Component1 component1 = new Component1();
private Component2 component2 = new Component2();
}
І якщо ви хочете виставити певні методи, визначте їх і дозвольте їм делегувати виклик відповідному контролеру.
Тут інтерфейси можуть стати в нагоді - якщо Component1
реалізує інтерфейс Interface1
і Component2
реалізує Interface2
, ви можете визначити
class Main implements Interface1, Interface2
Так що ви можете використовувати об’єкти як взаємозамінні там, де це дозволяє контекст.
Це досить просто. Ви можете реалізувати кілька типів інтерфейсів. Так, наприклад, у вас може бути реалізація, List
яка також є екземпляром Deque
(а Java це робить ... LinkedList
).
Ви просто не можете успадкувати реалізації від кількох батьків (тобто розширити кілька класів). Декларації (підписи методів) не становлять проблем.
Ви знаєте що, виходячи з точки зору розробника JavaScript, який намагається зрозуміти, що, чорт візьми, відбувається з цими матеріалами, я хотів би вказати на кілька речей, і хтось, будь ласка, скажіть мені, чого я тут пропускаю, якщо я далеко від позначки.
Інтерфейси дуже прості. Тупо, шалено просто. Вони такі ж безглузді, шалено прості, як спочатку думають люди, саме тому існує стільки повторюваних запитань саме на цю тему, тому що одна причина їх використання була незрозумілою людям, які намагаються зробити їх більше, ніж вони є і там є широко розповсюдженим зловживанням у кожній кодовій базі сервера Java, якій я коли-небудь стикався.
То чому ви хочете їх використовувати? Більшу частину часу ви цього не робили. Ви, звичайно, не хотіли б використовувати їх ВСЕ час, як здається багатьом. Але перш ніж я дістанусь до того, коли ви це зробите, давайте поговоримо про те, чого вони НЕ.
Інтерфейси НЕ:
Вони справді настільки прості, як ви думаєте, на перший погляд. Люди весь час тупо зловживають, тому важко зрозуміти, в чому справа. Це просто перевірка / тестування. Після того, як ви написали щось, що відповідає інтерфейсу і працює, видалення коду "реалізації" нічого не порушить.
Але якщо ви використовуєте інтерфейси правильно, ви не хотіли б видаляти його, тому що наявність його надає наступному розробнику інструмент для написання рівня доступу для іншого набору баз даних або веб-служб, для яких вони хочуть, щоб решта вашого додатка продовжувалась використовуючи, тому що вони знають, що їх клас зазнає невдачі, поки вони не встановлять 100% повний, як очікувалося, інтерфейс. Усі інтерфейси - це перевірка вашого класу та встановлення того, що ви насправді реалізували інтерфейс, як і обіцяли. Нічого більше.
Вони також портативні. Виставляючи дефініції вашого інтерфейсу, ви можете надати людям, які хочуть використовувати ваш невикритий код, набір методів, яким слід відповідати, щоб їх об'єкти могли його правильно використовувати. Вони не повинні реалізовувати інтерфейси. Вони могли просто записати їх на аркуші паперу для блокнотів і ще раз перевірити це. Але з інтерфейсом ви більше гарантуєте, що нічого не намагатиметься працювати, поки у нього не буде належної версії відповідного інтерфейсу.
Отже, будь-який інтерфейс, який, мабуть, ніколи не буде реалізований більше одного разу? Зовсім марний. Багаторазове успадкування? Перестань тягнутися до цієї веселки. Java, насамперед, уникає їх з причини, і складені / агреговані об'єкти в будь-якому випадку є більш гнучкими у багатьох відношеннях. Це не означає, що інтерфейси не можуть допомогти вам моделювати способи, які дозволяє множинне успадкування, але насправді це не успадкування у будь-якій формі або формі і не повинно розглядатися як таке. Це лише гарантія того, що ваш код не працюватиме, поки ви не застосуєте всі встановлені вами методи.
Y
також повинен мати можливість приймати посилання на X
. Якої користі мали б такі стосунки, якби така заміна була неможливою?
Це не симуляція багаторазового успадкування. У Java ви не можете успадковувати з двох класів, але якщо ви реалізуєте два інтерфейси, "здається, що ви успадкували від двох різних класів", оскільки ви можете використовувати свій клас як будь-який з ваших двох інтерфейсів.
Наприклад
interface MyFirstInteface{
void method1();
}
interface MySecondInteface{
void method2();
}
class MyClass implements MyFirstInteface, MySecondInteface{
public void method1(){
//Method 1
}
public void method2(){
//Method 2
}
public static void main(String... args){
MyFirstInterface mfi = new MyClass();
MySecondInterface msi = new MyClass();
}
}
Це буде працювати, і ви можете використовувати mfi та msi, це здається мультиспадковим, але це не тому, що ви нічого не успадковуєте, ви просто переписуєте загальнодоступні методи, надані інтерфейсами.
Потрібно бути точним:
Java дозволяє багаторазове успадкування інтерфейсу, але лише одне успадкування реалізації.
Ви виконуєте багаторазове успадкування інтерфейсу в Java так:
public interface Foo
{
String getX();
}
public interface Bar
{
String getY();
}
public class MultipleInterfaces implements Foo, Bar
{
private Foo foo;
private Bar bar;
public MultipleInterfaces(Foo foo, Bar bar)
{
this.foo = foo;
this.bar = bar;
}
public String getX() { return this.foo.getX(); }
public String getY() { return this.bar.getY(); }
}
До речі, причина, чому Java не реалізує повне множинне успадкування, полягає в тому, що це створює неоднозначності. Припустимо, ви можете сказати "A поширює B, C", і тоді як B, так і C мають функцію "void f (int)". Яку реалізацію успадковує A? За допомогою підходу Java ви можете реалізувати будь-яку кількість інтерфейсів, але інтерфейси оголошують лише підпис. Отже, якщо два інтерфейси містять функції з однаковим підписом, прекрасно, ваш клас повинен реалізувати функцію з цим підписом. Якщо інтерфейси, які ви успадковуєте, мають функції з різними підписами, то функції не мають нічого спільного між собою, тому не може бути й мови про конфлікт.
Я не кажу, що це єдиний спосіб. С ++ реалізує справжнє багаторазове успадкування, встановлюючи правила пріоритету, реалізація яких перемагає. Але автори Java вирішили усунути двозначність. Чи то через філософські переконання, що це створило чистіший код, чи тому, що вони не хотіли виконувати всю додаткову роботу, я не знаю.
Нечесно сказати, що інтерфейси «імітують» множинне успадкування.
Звичайно, ваш тип може реалізовувати кілька інтерфейсів і діяти як багато різних типів поліморфно. Однак, очевидно, ви не успадковуватимете поведінку або реалізації відповідно до цієї угоди.
Зазвичай дивіться на склад, де, на вашу думку, вам може знадобитися багаторазове успадкування.
АБО потенційним рішенням для досягнення чогось багаторазового успадкування, наприклад, є інтерфейс Mixin - http://csis.pace.edu/~bergin/patterns/multipleinheritance.html . Використовуйте обережно!
Вони цього не роблять.
Я думаю, що плутанина походить від людей, які вірять, що впровадження інтерфейсу є певною формою успадкування. Це не так; реалізація може бути просто порожньою, жодна поведінка не вимушена вчинком або гарантована будь-яким контрактом. Типовим прикладом є інтерфейс Clonable, який, маючи на увазі безліч чудових функціональних можливостей, що визначає настільки мало, що він по суті марний і потенційно небезпечний.
Що ви успадковуєте, реалізуючи інтерфейс? Бубкс! Тому, на мою думку, припиніть вживати слова інтерфейс та спадкування в одному реченні. Як сказав Майкл Борґвардт, інтерфейс - це не визначення, а аспект.
Ви можете наслідувати декілька конкретних класів, якщо вони самі реалізують інтерфейси. innerclasses
допоможе вам досягти цього:
interface IBird {
public void layEgg();
}
interface IMammal {
public void giveMilk();
}
class Bird implements IBird{
public void layEgg() {
System.out.println("Laying eggs...");
}
}
class Mammal implements IMammal {
public void giveMilk() {
System.out.println("Giving milk...");
}
}
class Platypus implements IMammal, IBird {
private class LayingEggAnimal extends Bird {}
private class GivingMilkAnimal extends Mammal {}
private LayingEggAnimal layingEggAnimal = new LayingEggAnimal();
private GivingMilkAnimal givingMilkAnimal = new GivingMilkAnimal();
@Override
public void layEgg() {
layingEggAnimal.layEgg();
}
@Override
public void giveMilk() {
givingMilkAnimal.giveMilk();
}
}
Я хотів би відзначити щось, що мене вкусило ззаду, що виходить із С ++, де ви також можете легко успадкувати багато реалізацій.
Наявність "широкого" інтерфейсу з багатьма методами означає, що вам доведеться впровадити багато методів у ваші конкретні класи, і ви не зможете легко ними поділитися між різними реалізаціями.
Наприклад:
interface Herbivore {
void munch(Vegetable v);
};
interface Carnivore {
void devour(Prey p);
}
interface AllEater : public Herbivore, Carnivore { };
class Fox implements AllEater {
...
};
class Bear implements AllEater {
...
};
У цьому прикладі Fox і Bear не можуть використовувати спільну базову реалізацію як для методів інтерфейсу, так munch
і дляdevour
.
Якщо базові реалізації виглядають так, можливо, ми хотіли б використовувати їх для Fox
та Bear
:
class ForestHerbivore implements Herbivore
void munch(Vegetable v) { ... }
};
class ForestCarnivore implements Carnivore
void devour(Prey p) { ... }
};
Але ми не можемо успадкувати їх обох. Базові реалізації повинні бути змінними-членами класу, і визначені методи можуть пересилати це. Тобто:
class Fox implements AllEater {
private ForestHerbivore m_herbivore;
private ForestCarnivore m_carnivore;
void munch(Vegetable v) { m_herbivore.munch(v); }
void devour(Prey p) { m_carnivore.devour(p); }
}
Це стає громіздким, якщо інтерфейси ростуть (тобто більше 5-10 методів ...)
Кращий підхід - визначити інтерфейс як сукупність інтерфейсів:
interface AllEater {
Herbivore asHerbivore();
Carnivore asCarnivore();
}
Це означає, що Fox
і Bear
лише має реалізовувати ці два методи, а інтерфейси та базові класи можуть рости незалежно від сукупностіAllEater
інтерфейсу, що стосується реалізаційних класів.
Менше зв’язування таким чином, якщо це працює для вашої програми.
Я не думаю, що вони це роблять.
Спадщина - це конкретно взаємозв'язок, спрямований на реалізацію між реалізаціями. Інтерфейси взагалі не надають ніякої інформації про реалізацію, а натомість визначають тип. Щоб отримати спадщину, потрібно спеціально успадкувати деякі типи поведінки або атрибути від батьківського класу.
Я вважаю, що тут десь конкретно є питання про роль інтерфейсів та множинного успадкування, але зараз я не можу його знайти ...
Насправді в Java немає моделювання множинного успадкування.
Люди іноді кажуть, що ви можете імітувати багаторазове успадкування за допомогою інтерфейсів, оскільки ви можете реалізувати більше одного інтерфейсу на клас, а потім використовувати композицію (а не успадкування) у своєму класі, щоб досягти поведінки кількох класів, від яких ви намагалися успадкувати. починати з.
Якщо це має сенс у вашій об'єктній моделі, ви, звичайно, можете успадкувати один клас і також реалізувати 1 або більше інтерфейсів.
Бувають випадки, коли багаторазове успадкування виявляється дуже зручним і його важко замінити інтерфейсами без написання додаткового коду. Наприклад, є програми для Android, які використовують класи, похідні від Activity, а інші - від FragmentActivity в тому самому додатку. Якщо у вас є певна функція, якою ви хочете поділитися в загальному класі, в Java вам доведеться дублювати код, а не дозволяти дочірні класи Activity та FragmentsActivity походити з того самого класу SharedFeature. І погана реалізація дженериків в Java також не допомагає, оскільки написання наступного є незаконним:
public class SharedFeature<T> extends <T extends Activity>
...
...
У Java немає підтримки багаторазового успадкування.
Ця історія підтримки множинного успадкування за допомогою інтерфейсу - це те, що ми розробили розробники. Інтерфейс надає гнучкість, ніж конкретні класи, і ми маємо можливість реалізувати кілька інтерфейсів, використовуючи один клас. Це за домовленістю, ми дотримуємось двох проектів для створення класу.
Це намагається наблизити до багаторазового успадкування. Що ми робимо, це реалізація декількох інтерфейсів, тут ми нічого не розширюємо (успадковуємо). Реалізуючий клас - це той, який збирається додати властивості та поведінку. Це не звільнення реалізації від батьківських класів. Я б просто сказав, що у Java немає підтримки багаторазового успадкування.
Ні, Java не підтримує множинне успадкування. Ні за допомогою класу, ні за допомогою інтерфейсу. Зверніться до цього посилання для отримання додаткової інформації https://devsuyed.wordpress.com/2016/07/21/does-java-support-multiple-inheritance
Я також повинен сказати, що Java не підтримує множинне успадкування.
Ви повинні розрізняти значення між extends
і implements
ключовими словами в Java. Якщо ми використовуємо extends
, ми фактично успадковуємо клас після цього ключового слова. Але для того, щоб зробити все простим, ми не можемо використовуватиextends
більше одного разу. Але ви можете реалізувати стільки інтерфейсів, скільки забажаєте.
Якщо ви реалізуєте інтерфейс, є нульовий шанс, що ви пропустите реалізацію всіх методів у кожному інтерфейсі (виняток: реалізації методів інтерфейсу за замовчуванням, введені в Java 8) Отже, ви тепер повністю усвідомлюєте, що відбувається з речами що ви внесли у свій новий клас.
Чому Java не дозволяє множинне успадкування насправді, багаторазове успадкування робить код дещо складним. Іноді два методи батьківських класів можуть конфліктувати через однакові підписи. Але якщо вас змусять впровадити всі методи вручну, ви отримаєте повне розуміння того, що відбувається, як я вже згадував вище. Це робить ваш код більш зрозумілим для вас.
Якщо вам потрібна додаткова інформація про інтерфейси Java, перегляньте цю статтю, http://www.geek-programmer.com/introduction-to-java-interfaces/
Між двома класами Java безпосереднє спадкування безпосередньо неможливе. У цьому випадку java рекомендує використовувати для інтерфейсу та оголошення методу всередині інтерфейсу та реалізації методу з класом Child.
interface ParentOne{
public String parentOneFunction();
}
interface ParentTwo{
public String parentTwoFunction();
}
class Child implements ParentOne,ParentTwo{
@Override
public String parentOneFunction() {
return "Parent One Finction";
}
@Override
public String parentTwoFunction() {
return "Parent Two Function";
}
public String childFunction(){
return "Child Function";
}
}
public class MultipleInheritanceClass {
public static void main(String[] args) {
Child ch = new Child();
System.out.println(ch.parentOneFunction());
System.out.println(ch.parentTwoFunction());
System.out.println(ch.childFunction());
}
}