Як інтерфейси Java імітують множинне успадкування?


76

Я читаю "Підручник з Java" (вдруге). Я щойно пройшов через розділ Інтерфейси (знову ж таки), але досі не розумію, як Інтерфейси Java імітують множинне успадкування. Чи є більш чітке пояснення, ніж те, що є в книзі?



У цій публікації в блозі описано, як досягти багаторазового успадкування. erik-rasmussen.com/blog/2006/10/23/multiple-inheritance-in-java
Ракеш Джуял

3
@Tom: відповідно до "Підручника Java" (4-е видання p142) "У Java клас може успадковувати лише один клас, але може реалізовувати більше одного інтерфейсу. Тому об'єкти можуть мати кілька типів: тип власного клас і типи всіх інтерфейсів, які вони реалізують. Це означає, що якщо змінна оголошена типом інтерфейсу, її значення може посилатися на будь-який об'єкт, який створюється з будь-якого класу, який реалізує інтерфейс. " Автор втратив мене в останньому реченні, але пояснення тут (здебільшого) це прояснили.
jacknad

Відповіді:


154

Припустимо, у вас є 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);

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

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


8
а якщо Кухня - це не інтерфейс, а інший клас?
Bizmarck

2
@bizmark: Потім створіть інтерфейс Cookery (with public void cook()), який би реалізовували і Kitchen, і PizzaTruck.
splungebob

10
Що робити, якщо і Truck, і кухня є класами з полями, і нам потрібні всі ці поля в PizzaTruck? наприклад, реалізувати PizzaTruck.shootPizzasBackwards (), що зменшує PizzaCount (поле для кухні) та збільшує швидкість (поле для вантажівки)
Hello World

Див. Склад щодо спадкування: en.wikipedia.org/wiki/Composition_over_inheritance
Пітер Тіллеманс

1
Незважаючи на правильність у 2010 році, "... які є абстрактними для всіх намірів і цілей без полів чи реалізацій методів ..." , більше не є правильним завдяки defaultметодам. Інтерфейси по- , як і раніше не мають стану екземпляра, але вони роблять є методи клас може успадкувати.
TJ Crowder,

19

Вас, мабуть, бентежить, оскільки ви переглядаєте множинне успадкування локально, з точки зору того, що один клас успадковує деталі реалізації від кількох батьків. Це неможливо на Java (і часто призводить до зловживань на мовах, де це можливо).

Інтерфейси дозволяють багаторазове успадкування типів , наприклад a class Waterfowl extends Bird implements Swimmerможе використовуватися іншими класами, як якщо б це булоBird і ніби це a Swimmer. У цьому полягає глибший сенс багаторазового успадкування: дозволяти одному об’єкту діяти так, ніби він належить одночасно кільком не пов’язаним між собою різним класам.


1
Посилено документами Oracle: docs.oracle.com/javase/tutorial/java/IandI/… . Щиро дякую за те, що зробили різницю між успадкуванням типів та більш "традиційним" визначенням успадкування, яке включає реалізацію. Я намагався зрозуміти, як інтерфейси можна вважати формою багаторазового успадкування протягом днів, і тепер це зрозуміло. Java просто використовує інше визначення успадкування, ніж я очікував. Я хотів би дати вам на 61 голос більше голосів, тому що, на мою думку, це сильніша відповідь, ніж прийнята зараз.
skrrgwasme

17

Ось спосіб досягти багаторазового успадкування через інтерфейси в 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();
}

Дуже дякую. Це відповідь для мене.
enadun

1
@challenger Коли ви реалізуєте "cInterface", нам потрібно буде реалізувати "getValueC ()" у класі A, чи не так?
Нік

10

враховуючи два інтерфейси нижче ...

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

Ми НЕ МОЖЕМО розширити два об’єкти, але ми можемо реалізувати два інтерфейси.


Що робити, якщо два методи в I1 та I2 мають однаковий тип аргументів?
Nivetha T

Тоді у вас був би лише один метод, компілятору буде байдуже, оскільки вони роблять те саме. Дивіться тут відповідь stackoverflow.com/questions/2801878 / ...
david99world

10

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

Якщо ви хочете поєднати функціональність двох класів в один - використовуйте композицію об’єкта. Тобто

public class Main {
    private Component1 component1 = new Component1();    
    private Component2 component2 = new Component2();
}

І якщо ви хочете виставити певні методи, визначте їх і дозвольте їм делегувати виклик відповідному контролеру.

Тут інтерфейси можуть стати в нагоді - якщо Component1реалізує інтерфейс Interface1і Component2реалізує Interface2, ви можете визначити

class Main implements Interface1, Interface2

Так що ви можете використовувати об’єкти як взаємозамінні там, де це дозволяє контекст.


5

Це досить просто. Ви можете реалізувати кілька типів інтерфейсів. Так, наприклад, у вас може бути реалізація, Listяка також є екземпляром Deque(а Java це робить ... LinkedList).

Ви просто не можете успадкувати реалізації від кількох батьків (тобто розширити кілька класів). Декларації (підписи методів) не становлять проблем.


Так, саме тому він "імітує" множинне успадкування.
Ерік Робертсон,

1
@Erik: насправді реалізовано багаторазове успадкування, а не багаторазове успадкування реалізації.
Йоахім Зауер,

@Erick: "Імітує" - це корисний термін лише тоді, коли ви заздалегідь уявляєте, яким має бути "ІМ" з іншої мови. Навіть тоді це руйнівна думка. Це багаторазове успадкування, просто унікально зроблене.
Mark Peters

Ну, він успадковує підписи, але не реалізацію. Якщо вам не подобається термін "модельоване множинне успадкування", як щодо "часткового багаторазового успадкування"? Це явно не те саме, що повне багаторазове успадкування. Мені здається, що найважливішою частиною є те, що якщо ви скажете "A розширює B реалізує C", ви можете мати функцію, яка приймає параметр типу C і передає йому A. C - цілком хороша декларація типу параметра, навіть якщо вона не постачається з декларацією. Можливо, ви не отримаєте іграшку, але принаймні отримаєте коробку.
Джей,

Власне, я збираюся відмовитись від цього. Я думаю, що це простіше і правильніше, якщо "успадкування" зберігається суто для розмови про реалізацію успадкування.
Mark Peters

5

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

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

То чому ви хочете їх використовувати? Більшу частину часу ви цього не робили. Ви, звичайно, не хотіли б використовувати їх ВСЕ час, як здається багатьом. Але перш ніж я дістанусь до того, коли ви це зробите, давайте поговоримо про те, чого вони НЕ.

Інтерфейси НЕ:

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

Вони справді настільки прості, як ви думаєте, на перший погляд. Люди весь час тупо зловживають, тому важко зрозуміти, в чому справа. Це просто перевірка / тестування. Після того, як ви написали щось, що відповідає інтерфейсу і працює, видалення коду "реалізації" нічого не порушить.

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

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

Отже, будь-який інтерфейс, який, мабуть, ніколи не буде реалізований більше одного разу? Зовсім марний. Багаторазове успадкування? Перестань тягнутися до цієї веселки. Java, насамперед, уникає їх з причини, і складені / агреговані об'єкти в будь-якому випадку є більш гнучкими у багатьох відношеннях. Це не означає, що інтерфейси не можуть допомогти вам моделювати способи, які дозволяє множинне успадкування, але насправді це не успадкування у будь-якій формі або формі і не повинно розглядатися як таке. Це лише гарантія того, що ваш код не працюватиме, поки ви не застосуєте всі встановлені вами методи.


Наслідування класу поєднує в собі два дещо ортогональні поняття: (1) можливість похідних класів використовувати члени базового класу як свої; (2) можливість для змінної типу базового класу зберігати посилання на об'єкти будь-якого похідного типу. Аспект №2 є досить ключовою частиною успадкування, яку я б, безсумнівно, назвав "подібною до успадкування" [тим більше, що аспект №1 є зручністю, але аспект №2 необхідний для ООП]. Основна мета інтерфейсів - дозволити коду з посиланням на річ, яка, як відомо, має певну функціональність, використовувати цю функціональність .
supercat

2 більше схожий на ключовий аспект Java OOP через інші обмеження дизайну мови, ніж на ключовий аспект ООП або успадкування загалом. Хоча я щойно виявив, що ви можете визначати та успадковувати константи з інтерфейсу в Java, і я б назвав це більше, ніж схоже на успадкування.
Ерік Реппен

Відношення "X-is-aY", визначене успадкуванням, визначає, що код, який може приймати посилання на a, Yтакож повинен мати можливість приймати посилання на X. Якої користі мали б такі стосунки, якби така заміна була неможливою?
supercat

"Після того, як ви написали щось, що відповідає інтерфейсу і працює, видалення коду" реалізує "нічого не порушить." -> Це невірно, що особливо очевидно при роботі з інтерфейсами , які є порожніми (наприклад , в маркерні інтерфейси: stackoverflow.com/questions/25850328/marker-interfaces-in-java )
hmijail сумує resignees

3

Це не симуляція багаторазового успадкування. У 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, це здається мультиспадковим, але це не тому, що ви нічого не успадковуєте, ви просто переписуєте загальнодоступні методи, надані інтерфейсами.


3

Потрібно бути точним:

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

3

До речі, причина, чому Java не реалізує повне множинне успадкування, полягає в тому, що це створює неоднозначності. Припустимо, ви можете сказати "A поширює B, C", і тоді як B, так і C мають функцію "void f (int)". Яку реалізацію успадковує A? За допомогою підходу Java ви можете реалізувати будь-яку кількість інтерфейсів, але інтерфейси оголошують лише підпис. Отже, якщо два інтерфейси містять функції з однаковим підписом, прекрасно, ваш клас повинен реалізувати функцію з цим підписом. Якщо інтерфейси, які ви успадковуєте, мають функції з різними підписами, то функції не мають нічого спільного між собою, тому не може бути й мови про конфлікт.

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


Виправте мене, якщо я помиляюся, але з мого розуміння C ++ також виключає можливість додавати віртуальні члени до базового класу без перекомпіляції підкласів, а також позбавляється референтної ідентичності базового класу. Якщо B має віртуальний член Q, X і Y успадковують B і окремо замінюють Q, а Z успадковує X і Y, передача a & Z у a & X, а потім & B дасть інший результат від передачі a & Z у a & Y, а потім & B .
supercat

2

Нечесно сказати, що інтерфейси «імітують» множинне успадкування.

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

Зазвичай дивіться на склад, де, на вашу думку, вам може знадобитися багаторазове успадкування.

АБО потенційним рішенням для досягнення чогось багаторазового успадкування, наприклад, є інтерфейс Mixin - http://csis.pace.edu/~bergin/patterns/multipleinheritance.html . Використовуйте обережно!


2

Вони цього не роблять.

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

Що ви успадковуєте, реалізуючи інтерфейс? Бубкс! Тому, на мою думку, припиніть вживати слова інтерфейс та спадкування в одному реченні. Як сказав Майкл Борґвардт, інтерфейс - це не визначення, а аспект.


2

Ви можете наслідувати декілька конкретних класів, якщо вони самі реалізують інтерфейси. 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();
    }

}


2

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

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

Наприклад:

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 інтерфейсу, що стосується реалізаційних класів.

Менше зв’язування таким чином, якщо це працює для вашої програми.


1

Я не думаю, що вони це роблять.

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

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


1

Насправді в Java немає моделювання множинного успадкування.

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


1

Якщо це має сенс у вашій об'єктній моделі, ви, звичайно, можете успадкувати один клас і також реалізувати 1 або більше інтерфейсів.


1

Бувають випадки, коли багаторазове успадкування виявляється дуже зручним і його важко замінити інтерфейсами без написання додаткового коду. Наприклад, є програми для Android, які використовують класи, похідні від Activity, а інші - від FragmentActivity в тому самому додатку. Якщо у вас є певна функція, якою ви хочете поділитися в загальному класі, в Java вам доведеться дублювати код, а не дозволяти дочірні класи Activity та FragmentsActivity походити з того самого класу SharedFeature. І погана реалізація дженериків в Java також не допомагає, оскільки написання наступного є незаконним:

public class SharedFeature<T> extends <T extends Activity>

...
...

1

У Java немає підтримки багаторазового успадкування.

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

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



1

Я також повинен сказати, що Java не підтримує множинне успадкування.

Ви повинні розрізняти значення між extendsі implementsключовими словами в Java. Якщо ми використовуємо extends, ми фактично успадковуємо клас після цього ключового слова. Але для того, щоб зробити все простим, ми не можемо використовуватиextends більше одного разу. Але ви можете реалізувати стільки інтерфейсів, скільки забажаєте.

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

Чому Java не дозволяє множинне успадкування насправді, багаторазове успадкування робить код дещо складним. Іноді два методи батьківських класів можуть конфліктувати через однакові підписи. Але якщо вас змусять впровадити всі методи вручну, ви отримаєте повне розуміння того, що відбувається, як я вже згадував вище. Це робить ваш код більш зрозумілим для вас.

Якщо вам потрібна додаткова інформація про інтерфейси Java, перегляньте цю статтю, http://www.geek-programmer.com/introduction-to-java-interfaces/


1

Між двома класами 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());
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.