Как интерфейсы Java имитируют множественное наследование? - PullRequest
73 голосов
/ 24 августа 2010

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

Ответы [ 21 ]

149 голосов
/ 24 августа 2010

Предположим, у вас есть 2 вида вещей в вашем домене: Грузовики и Кухни

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

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

В 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);

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

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

18 голосов
/ 24 августа 2010

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

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

16 голосов
/ 03 февраля 2013

Вот способ достижения множественного наследования через интерфейсы в 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();
}
10 голосов
/ 24 августа 2010

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

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 августа 2010

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

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

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

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

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

class Main implements Interface1, Interface2

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

6 голосов
/ 05 февраля 2013

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

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

Итак, почему вы хотите их использовать? Большую часть времени вы не будете. Вы, конечно, не захотите использовать их ВСЕ время, как многие думают. Но прежде чем я доберусь до вас, давайте поговорим о том, чем они НЕ являются.

Интерфейсы НЕ являются:

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

Они действительно так просты, как вы думаете, на первый взгляд. Люди все время тупо злоупотребляют, поэтому трудно понять, в чем суть. Это просто проверка / проверка. После того, как вы написали что-то, соответствующее интерфейсу и работающее, удаление этого «реализующего» кода ничего не сломает.

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

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

Итак, какой интерфейс вряд ли когда-либо будет реализован более одного раза? Совершенно бесполезно. Многократное наследование? Хватит тянуться к этой радуге. Во-первых, Java избегает их по какой-то причине, а составные / агрегированные объекты в любом случае более гибки. Это не значит, что интерфейсы не могут помочь вам моделировать способами, которые допускают множественное наследование, но на самом деле это не наследование в любом виде или форме, и его не следует рассматривать как таковое. Это просто гарантия того, что ваш код не будет работать, пока вы не реализовали все методы, которые вы установили, как это было бы.

5 голосов
/ 24 августа 2010

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

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

3 голосов
/ 24 августа 2010

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

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

3 голосов
/ 24 августа 2010

Это не симуляция множественного наследования.В 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 августа 2010

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

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