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


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

21 71

21 ответ:

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

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

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

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

поэтому в Java мы склонны реализовывать множественное наследование с помощью делегирования:

Паули подклассы грузовик и добавляет кухню к грузовику в переменной-члене под названием Кухня. Он реализует кухонный интерфейс, вызывая кухню.готовить.)(

class PizzaTruck extends Truck implements Kitchen {
   Kitchen kitchen;

   public void cook(Food foodItem) {
      kitchen.cook(foodItem);
   }
}

он счастливый человек, потому что теперь он может делать такие вещи, как ;

pizzaTruck.driveTo(beach);
pizzaTruck.cook(pizzaWithExtraAnchovies);

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

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

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

интерфейсы позволяют множественное наследование типы, например a class Waterfowl extends Bird implements Swimmer может использоваться другими классами, как если бы это было Birdи как 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

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

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

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

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

интерфейсы не являются:

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

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

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

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

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

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

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

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

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)". Реализация не наследуют? С подходом Java вы можете реализовать любое количество интерфейсов, но интерфейсы только объявляют подпись. Поэтому, если два интерфейса включают функции с одной и той же сигнатурой, отлично, ваш класс должен реализовать функцию с этой сигнатурой. Если интерфейсы, которые вы наследуете, имеют функции с разными сигнатурами, тогда функции не имеют ничего общего друг с другом, поэтому о конфликте не может быть и речи.

Я не говорю, что это единственный способ. C++ реализует истинное множественное наследование, устанавливая правила приоритета, реализация которых выигрывает. Но авторы 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();
    }

}

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

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

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

нет имитации множественного наследования в Java.

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

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

есть случаи, когда множественное наследование оказывается очень удобным и трудным для замены интерфейсами без написания дополнительного кода. Например, есть приложения для Android, которые используют классы, полученные из Activity и другие из FragmentActivity в том же приложении. Если у вас есть определенная функция, которую вы хотите использовать в общем классе, в Java вам придется дублировать код, а не позволять дочерним классам Activity и FragmentsActivity выводиться из одного и того же класса SharedFeature. И бедные реализация дженериков в Java также не помогает, потому что написание следующего является незаконным:

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

...
...

в java нет поддержки множественного наследования.

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

Это попытка приблизиться к множественному наследованию. То, что мы делаем, это реализовать несколько интерфейсов, вот мы не расширяя (наследуя) ничего. Реализующий класс-это тот, который будет добавлять свойства и поведение. Он не получает реализацию бесплатно от родительских классов. Я бы просто сказал, что в java нет поддержки множественного наследования.

Я хотел бы указать на то, что укусило меня сзади, исходя из C++, где вы можете легко наследовать много реализаций тоже.

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

например:

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 {
   ...
};

в этом примере Лиса и Медведь не могут совместно использовать общую базовую реализацию для оба это методы интерфейса 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 только должен реализовать эти два метода, и интерфейсы и базовые классы могут расти независимо от aggregate AllEater интерфейс, который касается реализации классов.

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

нет, 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/