Java接口如何模拟多重继承?


76

我正在阅读“ Java教程”(第2次)。我刚读完有关接口的部分,但仍然不了解Java接口如何模拟多重继承。有比书中更清楚的解释吗?



这篇博客文章解释了如何实现多重继承。erik-rasmussen.com/blog/2006/10/23/multiple-inheritance-in-java
Rakesh Juyal,2010年

3
@Tom:“ Java教程”,第4版,第142页,“在Java中,一个类只能从一个类继承,但可以实现多个接口。因此,对象可以具有多种类型:自己的类型类及其实现的所有接口的类型。这意味着,如果将变量声明为接口的类型,则其值可以引用从实现该接口的任何类实例化的任何对象。” 作者在最后一句话中迷失了我,但此处的解释(大部分是)使问题得以解决。
jacknad 2010年

Answers:


153

假设您的域中有两种东西:卡车和厨房

卡车具有driveTo()方法,而Kitchens具有cook()方法。

现在,假设Pauli决定从送货卡车的后面出售比萨饼。他想要一种可以与drive()和cook()一起使用的东西。

在C ++中,他将使用多重继承来做到这一点。

在Java中,这被认为太危险了,因此您可以从主类继承,但是可以从接口“继承”行为,这些行为对于所有意图和目的都是抽象类,没有字段或方法实现。

因此,在Java中,我们倾向于使用委托来实现多重继承:

Pauli对卡车进行了子类化,并在一个名为厨房的成员变量中为卡车添加了厨房。他通过调用kitchen.cook()来实现Kitchen接口。

class PizzaTruck extends Truck implements Kitchen {
   Kitchen kitchen;

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

他是一个快乐的人,因为他现在可以做类似的事情;

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

好的,这个愚蠢的故事是要指出它不是多重继承的模拟,它是真正的多重继承,附带条件是您只能继承协定,只能从称为接口的空抽象基类继承。

(更新:随着默认方法的出现,现在接口也可以提供一些要继承的行为)


8
如果Kitchen不是接口而是另一个类怎么办?
Bizmarck

1
然后它将炸毁。
亚当·阿罗德

2
@bizmark:然后创建public void cook()Kitchen和PizzaTruck都将实现的Cookery接口(带有)。
splungebob

10
如果卡车和厨房都是具有字段的类,而我们需要PizzaTruck中的所有这些字段怎么办?例如,实现PizzaTruck.shootPizzasBackwards(),从而减少PizzaCount(在“厨房”字段中)并提高速度(在“卡车”字段中)
Hello World

1
尽管在2010年是正确的,但由于有了方法,“ ...对于所有意图和目的都没有字段或方法实现的抽象类……”不再正确default。接口仍然没有实例状态,但是它们确实具有类可以继承的方法。
TJ Crowder

19

您可能会感到困惑,因为您会在本地查看多个继承,就一个类而言,它是从多个父级继承实现细节的。在Java中这是不可能的(并且在可能的情况下经常导致语言滥用)。

接口允许类型的多重继承,例如aclass Waterfowl extends Bird implements Swimmer可以被其他类当作aBird 如同a一样使用Swimmer。这是多重继承的更深层含义:允许一个对象像它一次一样属于多个不相关的不同类。


1
由Oracle的文档加强:docs.oracle.com/javase/tutorial/java/IandI/…。我非常感谢您区分类型的继承继承的更“传统”的定义,其中包括实现。我一直试图了解如何将接口视为多继承的形式已有好几天了,现在很清楚了。Java只是使用了与我预期不同的继承定义。我希望我能再给您61票赞成票,因为我认为这是比目前接受的答复更强有力的答复。
skrrgwasme,2015年

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 2014年

1
@challenger当您实现“ cInterface”时,我们将需要在A类中实现“ getValueC()”,对吗?
尼克

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实现接口Interface1Component2工具Interface2,则可以定义

class Main implements Interface1, Interface2

这样您就可以在上下文允许的地方互换使用对象。


5

很简单 您可以在一种类型中实现多个接口。因此,例如,您可能有一个实现,List它也是的一个实例Deque(而Java确实... LinkedList)。

您只是不能从多个父级继承实现(即扩展多个类)。 声明(方法签名)没有问题。


是的,这就是为什么它“模拟”多重继承的原因。
艾里克·罗伯逊

1
@Erik:它实际上实现了多重继承,而不是实现的多重继承。
约阿希姆·绍尔

@Erick:“ Simulates”仅在您对另一种语言的MI应该“如何”具有先入为主的观念时才是一个有用的术语。即使那样,这也是一个破坏性的想法。它是多重继承,只是唯一完成。
马克·彼得斯

好吧,它继承了签名而不是实现。如果您不喜欢“模拟多重继承”一词,那么“部分多重继承”又如何呢?显然,它与完整的多重继承不同。在我看来,最重要的部分是,如果您说“ A扩展了B实现C”,那么您就可以拥有一个接受类型为C的参数并将其传递给A的函数。C是一个非常好的参数类型声明,即使它没有声明也是如此。您可能没有得到玩具,但至少得到了包装盒。
杰伊,2010年

实际上,我将在这一方面回击。我认为,如果严格地保留“继承性”用于谈论继承实现,则将更加简单,正确。
马克·彼得斯

5

您知道什么,从一个JavaScript开发人员试图了解这些东西到底发生了什么的角度出发,我想指出几点,如果有人愿意,请告诉我我在这里缺少的内容偏离标记。

接口真的很简单。愚蠢地,疯狂地简单。它们像人们最初想象的那样愚蠢,疯狂,这就是为什么在这个确切的主题上存在如此多重复的问题的原因,因为人们试图使它们的使用量超出实际,从而使人们不清楚使用它们的一个原因。在我接触过的每个Java服务器端代码库中,普遍存在滥用。

那么,为什么要使用它们呢?大多数时候你不会。您当然不希望像许多人所想的那样一直使用它们。但是,在我问您何时选择之前,让我们先谈谈它们不是什么。

接口不是:

  • 以任何方式解决Java缺少的任何种类的继承机制的问题。他们与继承无关,从来没有做过,也没有模拟任何类似继承的事情。
  • 一定可以帮助您编写自己的东西,同时也可以帮助其他人编写可以与您的东西交互的东西。

乍一看,它们真的很简单。人们总是愚蠢地滥用,所以很难理解重点是什么。这只是验证/测试。一旦编写了一些符合接口并可以工作的东西,删除该“实现”代码就不会破坏任何东西。

但是,如果您正确使用了接口,则不希望将其删除,因为在那里拥有接口可以为下一个开发人员提供工具,为他们希望继续应用程序其余部分的另一组数据库或Web服务编写访问层。使用,因为他们知道自己的课程将失败,直到他们获得100%的“按预期完成”界面。所有接口的作用都是验证您的类并确定您实际上已经实现了您承诺的接口。而已。

它们也是便携式的。通过公开您的接口定义,您可以为希望使用未公开代码的人们提供一组遵循的方法,以便他们的对象正确使用它。他们不必实现接口。他们可以将它们记在一张记事本纸上,然后仔细检查。但是,有了该接口,您将更有保证,直到它具有适当版本的接口后,您才能尝试工作。

那么,任何接口都不可能实现一次以上?完全没用。多重继承?停止伸手去拿那彩虹。Java首先避免了它们的出现,无论如何,复合/聚合对象在许多方面都更加灵活。并不是说接口不能以多重继承允许的方式帮助您建模,但实际上它不是任何形式或形式的继承,因此不应被视为如此。这只是保证您的代码在实现实现的所有方法之前不会起作用。


类继承结合了两个有点正交的概念:(1)派生类使用基类成员的能力就好像它们是自己的一样;(2)基类类型的变量保留对任何派生类型的对象的引用的能力。方面#2是继承的足够关键的部分,我肯定会称其为“类继承”(特别是因为方面#1是一种便利,但是OOP需要方面#2)。接口的基本目的是允许引用具有已知功能的事物的代码来使用该功能
2013年

由于其他语言设计约束,因此2感觉更像是Java OOP的关键方面,而不是OOP或继承的关键方面。尽管我确实发现您可以在Java接口中定义和继承常量,但我称其为类继承之外。
Erik Reppen 2013年

继承定义的“ X-is-aY”关系指定可以接受对a的引用的代码也Y应该也可以接受对a的引用X。如果无法进行这种替换,那么这种关系有什么用?
2013年

“一旦您编写了一些符合接口并且可以工作的东西,删除“实现”代码就不会破坏任何东西。” ->这是错误的,这在处理空接口时尤其明显(例如在标记接口中:stackoverflow.com/questions/25850328/marker-interfaces-in-java
hmijail哀悼受聘者

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的方法,您可以实现任意数量的接口,但是接口仅声明一个签名。因此,如果两个接口都包含具有相同签名的函数,那很好,您的类必须实现具有该签名的函数。如果您继承的接口的功能具有不同的签名,则这些功能彼此无关,因此没有冲突的问题。

我并不是说这是唯一的方法。C ++通过建立优先实现规则来实现真正的多重继承。但是Java的作者决定消除歧义。我是不知道是因为出于哲学考虑,认为这样做是为了编写更清晰的代码,还是因为他们不想做所有额外的工作,所以不知道。


如果我错了,请指正我,但是从我的理解来看,C ++还消除了在不重新编译子类的情况下将虚拟成员添加到基类的能力,并且丧失了基类的引用身份。如果B具有虚拟成员Q,则X和Y都继承B并分别覆盖Q,并且Z继承X和Y两者,将&Z强制转换为&X,然后&B产生的结果不同于将&Z强制转换为&Y然后是&B的结果。 。
2013年

2

说接口“模拟”多重继承是不公平的。

当然,您的类型可以实现多个接口,并且可以多态地充当许多不同的类型。但是,您显然不会在这种安排下继承行为或实现。

通常在您可能需要多重继承的地方查看合成。

或实现类似多重继承的潜在解决方案是Mixin接口-http: //csis.pace.edu/~bergin/patterns/multipleinheritance.html。小心使用!


2

他们没有。

我认为这种混淆来自于人们认为实现接口构成某种形式的继承。没有 该实现可以完全是空白,该行为没有强制执行或通过任何合同保证的行为。一个典型的例子是Clonable接口,它暗示了很多强大的功能,但定义得很少,以至于它本质上是无用的,并且有潜在的危险。

通过实现接口,您继承了什么?b!因此,我认为,不要在同一句子中使用接口和继承一词。正如Michael Borgwardt所说,接口不是定义而是一个方面。


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

我想指出一些令人毛骨悚然的东西,它们来自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

如果基本实现看起来像这样,我们可能想将它们用于FoxBear

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不支持多重继承。

您必须在Java中区分extendsimplements关键字之间的含义。如果使用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());
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.