Как интерфейсы Java имитируют множественное наследование?
Я читаю "Учебник по Java" (второй раз). Я только что прошел раздел об интерфейсах (снова), но все еще не понимаю, как интерфейсы Java имитируют множественное наследование. Есть ли более четкое объяснение, чем то, что в книге?
21 ответ
Предположим, у вас есть 2 вида вещей в вашем домене: грузовики и кухни
В грузовиках есть метод driveTo(), а в Kitchens - метод 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);
Хорошо, эта глупая история заключалась в том, что это не симуляция множественного наследования, это реальное множественное наследование при условии, что вы можете наследовать только контракт, наследовать только от пустых абстрактных базовых классов, которые называются интерфейсами.
(обновление: с появлением методов по умолчанию интерфейсы теперь также могут обеспечивать некоторое наследование)
Вы, вероятно, сбиты с толку, потому что вы просматриваете множественное наследование локально с точки зрения одного класса, наследующего детали реализации от нескольких родителей. Это невозможно в Java (и часто приводит к злоупотреблениям в языках, где это возможно).
Интерфейсы допускают множественное наследование типов, например 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();
}
Интерфейсы не имитируют множественное наследование. Создатели Java считали множественное наследование неправильным, поэтому в Java такого нет.
Если вы хотите объединить функциональность двух классов в один - используйте композицию объектов. Т.е.
public class Main {
private Component1 component1 = new Component1();
private Component2 component2 = new Component2();
}
А если вы хотите предоставить определенные методы, определите их и позвольте им делегировать вызов соответствующему контроллеру.
Здесь могут пригодиться интерфейсы - если Component1
реализует интерфейс Interface1
а также Component2
инвентарь Interface2
Вы можете определить
class Main implements Interface1, Interface2
Так что вы можете использовать объекты взаимозаменяемо, где это позволяет контекст.
Учитывая два интерфейса ниже...
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");
}
}
Мы НЕ МОЖЕМ расширять два объекта, но мы можем реализовать два интерфейса.
Вы знаете, что с точки зрения разработчика JavaScript, пытающегося понять, что, черт возьми, происходит с этими вещами, я хотел бы указать на пару вещей, и кто-то, пожалуйста, скажите мне, что мне здесь не хватает, если я далеко от цели.
Интерфейсы действительно просты. Тупо, безумно просто. Они настолько глупы, безумно просты, как люди думают вначале, вот почему так много повторяющихся вопросов на эту тему, потому что люди, пытающиеся сделать из них больше, чем они есть, не поняли единственную причину их использования. Широко распространено злоупотребление в каждой кодовой базе Java-сервера, с которой я когда-либо сталкивался.
Итак, почему вы хотите их использовать? Большую часть времени вы не будете. Вы, конечно, не захотите использовать их ВСЕ время, как многие думают. Но прежде чем я доберусь до вас, давайте поговорим о том, чем они НЕ являются.
Интерфейсы НЕ являются:
- в любом случае обходной путь для любого вида механизма наследования, которого не хватает в Java. Они не имеют ничего общего с наследованием, они никогда не делали, и никоим образом не имитируют ничего подобного наследованию.
- обязательно что-то, что поможет вам с тем, что вы написали, настолько, насколько это поможет другому парню написать что-то, что должно быть связано с вашим материалом.
Они действительно так просты, как вы думаете, на первый взгляд. Люди все время тупо злоупотребляют, поэтому трудно понять, в чем суть. Это просто проверка / проверка. После того, как вы написали что-то, соответствующее интерфейсу и работающее, удаление этого "реализующего" кода ничего не сломает.
Но если вы используете интерфейсы правильно, вы не захотите удалять его, потому что наличие этого дает следующему разработчику инструмент для написания слоя доступа для другого набора баз данных или веб-сервисов, который они хотят, чтобы остальная часть вашего приложения продолжала использование, потому что они знают, что их класс потерпит неудачу, пока они не получат на 100% полный, как ожидалось, интерфейс. Все интерфейсы проверяют ваш класс и устанавливают, что вы фактически реализовали интерфейс, как и обещали. Ничего более.
Они также портативны. Предоставляя определения вашего интерфейса, вы можете дать людям, желающим использовать ваш неэкспонированный код, набор методов, которым они должны соответствовать, чтобы их объекты использовали его правильно. Им не нужно реализовывать интерфейсы. Они могли бы просто записать их на листе блокнота и перепроверить это. Но с интерфейсом у вас есть больше гарантий, что ничто не будет пытаться работать, пока у него не будет подходящей версии рассматриваемого интерфейса.
Итак, какой интерфейс вряд ли когда-либо будет реализован более одного раза? Совершенно бесполезно. Многократное наследование? Хватит тянуться к этой радуге. Во-первых, Java избегает их по какой-то причине, а составные / агрегированные объекты в любом случае более гибки. Это не значит, что интерфейсы не могут помочь вам моделировать способами, которые допускают множественное наследование, но на самом деле это не наследование в любом виде или форме, и его не следует рассматривать как таковое. Это просто гарантия того, что ваш код не будет работать, пока вы не реализовали все методы, которые вы установили, как это было бы.
Это довольно просто. Вы можете реализовать более одного интерфейса в типе. Так, например, вы могли бы иметь реализацию List
это также пример Deque
(и Java делает...LinkedList
).
Вы просто не можете наследовать реализации от нескольких родителей (то есть расширять несколько классов). Объявления (сигнатуры методов) не являются проблемой.
Это не симуляция множественного наследования. В 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 вы можете реализовать любое количество интерфейсов, но интерфейсы только объявляют подпись. Так что, если два интерфейса включают функции с одной и той же сигнатурой, хорошо, ваш класс должен реализовать функцию с этой сигнатурой. Если интерфейсы, которые вы наследуете, имеют функции с разными сигнатурами, то эти функции не имеют ничего общего друг с другом, поэтому не возникает вопроса о конфликте.
Я не говорю, что это единственный способ. C++ реализует истинное множественное наследование, устанавливая правила приоритета, реализация которых выигрывает. Но авторы 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 {
...
};
В этом примере 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
интерфейс, который касается реализации классов.
Меньше связывания таким образом, если это работает для вашего приложения.
На самом деле вы можете "наследовать" от нескольких конкретных классов, если они сами реализуют интерфейсы. 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();
}
}
Они не
Я думаю, что путаница исходит от людей, считающих, что реализация интерфейса представляет собой некоторую форму наследования. Это не так; реализация может быть просто пустой, поведение не навязывается действием или гарантируется каким-либо контрактом. Типичным примером является интерфейс Clonable, который ссылается на множество отличных функций, которые так мало определяют, что он по сути бесполезен и потенциально опасен.
Что вы наследуете, реализуя интерфейс? Bubkes! Поэтому, на мой взгляд, прекратите использовать слова интерфейс и наследование в одном предложении. Как сказал Майкл Боргвардт, интерфейс - это не определение, а аспект.
Нечестно сказать, что интерфейсы "имитируют" множественное наследование.
Несомненно, ваш тип может реализовывать несколько интерфейсов и действовать как много разных типов полиморфно. Тем не менее, вы, очевидно, не будете наследовать поведение или реализации в этом соглашении.
Обычно смотрите на композицию, где, по вашему мнению, может потребоваться множественное наследование.
ИЛИ Потенциальным решением для достижения чего-либо множественного наследования является интерфейс Mixin - http://csis.pace.edu/~bergin/patterns/multipleinheritance.html. Используйте с осторожностью!
В Java не поддерживается множественное наследование.
Мы разработали эту историю о поддержке множественного наследования с использованием интерфейса. Интерфейс дает гибкость, чем конкретные классы, и у нас есть возможность реализовать несколько интерфейсов, используя один класс. По соглашению мы придерживаемся двух чертежей для создания класса.
Это пытается приблизиться к множественному наследованию. Что мы делаем, так это реализуем множественный интерфейс, здесь мы ничего не расширяем (не наследуем). Реализующий класс - это тот, который собирается добавить свойства и поведение. Это не освобождение реализации от родительских классов. Я бы просто сказал, что в Java нет поддержки множественного наследования.
Я также должен сказать, что Java не поддерживает множественное наследование.
Вы должны различать значение между extends
а также implements
ключевые слова в Java. Если мы используем extends
мы на самом деле наследуем класс после этого ключевого слова. Но, чтобы сделать все просто, мы не можем использовать extends
больше чем единожды. Но вы можете реализовать столько интерфейсов, сколько пожелаете.
Если вы реализуете интерфейс, существует нулевая вероятность того, что вы пропустите реализацию всех методов в каждом интерфейсе (Исключение: реализации по умолчанию методов интерфейса, представленные в Java 8). Итак, теперь вы полностью осведомлены о том, что происходит с вещами. что вы вложили в свой новый класс.
Почему в Java не разрешено множественное наследование, множественное наследование делает код несколько сложным. Иногда два метода родительских классов могут конфликтовать из-за наличия одинаковых подписей. Но если вы вынуждены реализовать все методы вручную, вы получите полное представление о том, что происходит, как я уже упоминал выше. Это делает ваш код более понятным для вас.
Если вам нужна дополнительная информация об интерфейсах Java, ознакомьтесь с этой статьей, http://www.geek-programmer.com/introduction-to-java-interfaces/
В Java нет симуляции множественного наследования.
Иногда люди говорят, что вы можете имитировать множественное наследование с помощью интерфейсов, потому что вы можете реализовать более одного интерфейса для каждого класса, а затем использовать композицию (а не наследование) в своем классе для достижения поведения нескольких классов, которые вы пытались унаследовать от начать с.
Нет, Java не поддерживает множественное наследование. Ни используя класс, ни используя интерфейс. Обратитесь к этой ссылке для получения дополнительной информации https://devsuyed.wordpress.com/2016/07/21/does-java-support-multiple-inheritance
Я не думаю, что они делают.
Наследование - это, в частности, ориентированные на реализацию отношения между реализациями. Интерфейсы вообще не предоставляют никакой информации о реализации, но вместо этого определяют тип. Чтобы иметь наследование, вам нужно специально наследовать некоторые поведения или атрибуты от родительского класса.
Я полагаю, что где-то здесь есть вопрос конкретно о роли интерфейсов и множественного наследования, но я не могу найти его сейчас...
Если это имеет смысл в вашей объектной модели, вы, конечно, можете наследовать от одного класса и также реализовать 1 или более интерфейсов.
Есть случаи, когда множественное наследование оказывается очень удобным и трудным для замены интерфейсами без написания большего количества кода. Например, есть приложения для Android, которые используют классы, полученные из Activity, и другие из FragmentActivity в том же приложении. Если у вас есть особенность, которую вы хотите использовать в общем классе, в Java вам придется дублировать код, а не позволять дочерним классам Activity и FragmentsActivity наследоваться от одного и того же класса SharedFeature. И плохая реализация обобщений в Java также не помогает, потому что написание следующего является незаконным:
public class SharedFeature<T> extends <T extends Activity>
...
...
Между двумя классами 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());
}
}