Как Java-интерфейсы имитируют множественное наследование?

Я читаю "Учебник по Java" (второй раз). Я только что прошел раздел об интерфейсах (снова), но все еще не понимаю, как интерфейсы Java имитируют множественное наследование. Есть ли более четкое объяснение, чем то, что в книге?

74
задан user 13 May 2012 в 04:59
поделиться

13 ответов

Предположим, что в вашем домене есть 2 объекта: грузовики и кухни

Грузовики имеют метод driveTo(), а кухни — метод cook().

Теперь предположим, что Паули решает продавать пиццу из кузова грузовика. Ему нужна вещь, с помощью которой он может управлять функцией driveTo() и готовить().

В C++ для этого он использовал множественное наследование.

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

Итак, в Java мы склонны реализовывать множественное наследование с использованием делегирования:

Паули создает подкласс грузовика и добавляет к грузовику кухню в переменной-члене, называемой kitchen. Он реализует интерфейс 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);

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

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

151
ответ дан 24 November 2019 в 11:47
поделиться

Я не думаю, что они делают.

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

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

1
ответ дан 24 November 2019 в 11:47
поделиться

Вы должны быть точными:

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
ответ дан 24 November 2019 в 11:47
поделиться

Это не имитация множественного наследования. В 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
ответ дан 24 November 2019 в 11:47
поделиться

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

Вы просто не можете наследовать реализации от нескольких родителей (т. Е. Расширять несколько классов). Объявления (сигнатуры методов) не являются проблемой.

5
ответ дан 24 November 2019 в 11:47
поделиться

Интерфейсы не моделируют множественное наследование. Создатели Java считали множественное наследование неправильным, поэтому в Java такого нет.

Если вы хотите объединить функциональность двух классов в один - используйте композицию объектов. Т.е.

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

И если вы хотите предоставить определенные методы, определите их и позвольте им делегировать вызов соответствующему контроллеру.

Здесь могут пригодиться интерфейсы - если Component1 реализует интерфейс Interface1 и Component2 реализует Interface2 , вы можете определить

class Main implements Interface1, Interface2

So что вы можете взаимозаменяемо использовать объекты там, где это позволяет контекст.

10
ответ дан 24 November 2019 в 11:47
поделиться

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

Интерфейсы допускают множественное наследование типов , например Класс Waterfowl расширяет инструменты Bird. Swimmer может использоваться другими классами, как если бы он был Bird и , как если бы он был Swimmer . Это более глубокий смысл множественного наследования: позволить одному объекту действовать так, как будто он принадлежит сразу нескольким несвязанным различным классам.

18
ответ дан 24 November 2019 в 11:47
поделиться

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

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

Мы НЕ МОЖЕМ расширить два объекта, но мы можем реализовать два интерфейса.

10
ответ дан 24 November 2019 в 11:47
поделиться

Было бы несправедливо утверждать, что интерфейсы «моделируют» множественное наследование.

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

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

ИЛИ Потенциальным решением для достижения чего-то вроде множественного наследования является интерфейс Mixin - http://csis.pace.edu/~bergin/patterns/multipleinheritance.html . Используйте осторожно!

2
ответ дан 24 November 2019 в 11:47
поделиться

Кстати, причина, по которой Java не реализует полное множественное наследование, заключается в том, что оно создает двусмысленность. Предположим, вы могли бы сказать: «A расширяет B, C», и тогда и B, и C имеют функцию «void f(int)». Какую реализацию наследует A? С подходом Java вы можете реализовать любое количество интерфейсов, но интерфейсы объявляют только сигнатуру. Итак, если два интерфейса включают функции с одной и той же сигнатурой, хорошо, ваш класс должен реализовать функцию с этой сигнатурой. Если интерфейсы, которые вы наследуете, имеют функции с разными сигнатурами, то функции никак не связаны друг с другом, поэтому о конфликте не может быть и речи.

Я не говорю, что это единственный способ. C++ реализует истинное множественное наследование, устанавливая правила приоритета, чья реализация выигрывает. Но авторы Java решили устранить двусмысленность. То ли из-за философского убеждения, что это делает код чище, то ли из-за того, что они не хотели выполнять всю дополнительную работу, я не знаю.

3
ответ дан 24 November 2019 в 11:47
поделиться

Нет.

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

Что вы наследуете, реализуя интерфейс? Бабкес! Так что, на мой взгляд, перестаньте использовать слова интерфейс и наследование в одном предложении. Как сказал Майкл Боргвардт, интерфейс — это не определение, а аспект.

2
ответ дан 24 November 2019 в 11:47
поделиться

Если это имеет смысл в вашей объектной модели, вы, конечно, можете наследовать от одного класса и реализовать 1 или более интерфейсов.

1
ответ дан 24 November 2019 в 11:47
поделиться

В Java действительно нет моделирования множественного наследования.

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

1
ответ дан 24 November 2019 в 11:47
поделиться
Другие вопросы по тегам:

Похожие вопросы: