Java 为什么要使用继承呢?

Java 为什么要使用继承呢?,java,oop,language-agnostic,inheritance,composition,Java,Oop,Language Agnostic,Inheritance,Composition,我知道这个问题一直存在,但似乎总是基于这样的假设,即继承至少有时比组合更可取。我想挑战这个假设,希望获得一些理解 我的问题是:既然你可以用对象组合完成任何你可以用经典继承完成的事情,既然经典继承经常被滥用[1],既然对象组合让你可以灵活地更改委托对象运行时,你为什么要用经典继承? 我可以理解为什么你会推荐一些java语言和C++语言中的继承,这些语言不适合委派方便的语法。在这些语言中,只要不明显不正确,就可以通过使用继承来节省大量的输入。但是像Objective C和Ruby这样的其他语言提供了

我知道这个问题一直存在,但似乎总是基于这样的假设,即继承至少有时比组合更可取。我想挑战这个假设,希望获得一些理解


我的问题是:既然你可以用对象组合完成任何你可以用经典继承完成的事情,既然经典继承经常被滥用[1],既然对象组合让你可以灵活地更改委托对象运行时,你为什么要用经典继承?

<>我可以理解为什么你会推荐一些java语言和C++语言中的继承,这些语言不适合委派方便的语法。在这些语言中,只要不明显不正确,就可以通过使用继承来节省大量的输入。但是像Objective C和Ruby这样的其他语言提供了经典继承和非常方便的委托语法。据我所知,Go编程语言是唯一一种认为经典继承比它的价值更麻烦的语言,它只支持代码重用的委托

陈述我的问题的另一种方式是:即使您知道经典继承对于实现某个模型来说并不是不正确的,但这是否足以作为使用它而不是组合的理由

[1] 许多人使用经典继承来实现多态性,而不是让他们的类实现接口。继承的目的是代码重用,而不是多态性。此外,有些人使用继承来模拟他们对“是一种”关系的直观理解

更新

我只是想澄清一下,当我谈到继承时,我的确切意思是:

我是说。我并不是说从一个纯粹抽象的基类继承,它相当于实现一个接口,我没有反对这个接口

更新2


<>我知道继承是实现多态性C++的唯一途径。在这种情况下,很明显你为什么必须使用它。因此,我的问题仅限于Java或Ruby等语言,它们提供了实现多态性的不同方法(分别是接口和duck类型)。

使用继承的主要原因是而不是作为一种组合形式,这样您就可以获得多态行为。如果不需要多态性,则可能不应该使用继承,至少在C++中是这样。
  • 您需要公开所扩展类的整个API(通过委托,您将需要编写许多委托方法),并且您的语言没有提供一种简单的方式来表示“将所有未知方法委托给”
  • 对于没有“朋友”概念的语言,您需要访问受保护的字段/方法
  • 如果您的语言允许多重继承,那么委派的优势会有所减少
  • 如果您的语言允许在运行时从类甚至实例动态继承,则通常根本不需要委托。如果您可以控制同时公开哪些方法(以及如何公开),那么您根本不需要它
  • 我的结论是:委派是解决编程语言中bug的一种方法。

    您写道:

    [1] 许多人使用古典音乐 继承以实现多态性 而不是让他们的课 实现一个接口。目的 继承是代码重用,而不是 多态性。此外,有些人 使用继承来建模他们的 对“is-a”的直观理解 通常可以是 有问题

    在大多数语言中,“实现一个接口”和“从另一个接口派生一个类”之间的界限很窄。事实上,在C++语言中,如果你从类A派生类B,而A是纯纯方法所组成的类,那么你就要实现一个接口。 继承是关于接口重用,而不是实现重用。正如您在上面所写的,这与代码重用无关

    正如您正确指出的那样,继承意味着对is-A关系进行建模(事实上,许多人弄错了这一点与继承本身无关)。你也可以说“表现得像A”。然而,仅仅因为某个东西与其他东西有IS-A关系并不意味着它使用相同(甚至相似)的代码来实现这种关系

    < >比较C++实现不同数据输出方式的例子;两个类使用(公共)继承,因此可以通过多态方式访问它们:

    struct Output {
      virtual bool readyToWrite() const = 0;
      virtual void write(const char *data, size_t len) = 0;
    };
    
    struct NetworkOutput : public Output {
      NetworkOutput(const char *host, unsigned short port);
    
      bool readyToWrite();
      void write(const char *data, size_t len);
    };
    
    struct FileOutput : public Output {
      FileOutput(const char *fileName);
    
      bool readyToWrite();
      void write(const char *data, size_t len);
    };
    
    现在想象一下,如果这是Java。”“输出”不是结构,而是“接口”。它可能被称为“可写”。您可以说“implements Writable”,而不是“public Output”。就设计而言有什么不同


    无。

    接口只定义对象可以做什么,而不定义如何做。所以简单地说,接口就是契约。实现接口的所有对象都必须定义自己的契约实现。在实际世界中,这会给您提供
    关注点分离
    。想象一下,您正在编写一个应用程序,它需要处理各种您事先不知道的对象,但您仍然需要处理它们,您唯一知道的是这些对象应该做什么。因此,您将定义一个接口,并在合同中提及所有操作。现在,您将根据该接口编写应用程序。稍后,无论谁想要利用您的代码或应用程序,都必须在对象上实现接口,使其与您的系统一起工作。您的接口将强制其对象定义契约中定义的每个操作应该如何完成。通过这种方式,任何人都可以编写实现您的接口的对象,以便让它们完美地适应您的系统,您所知道的就是需要做什么,而需要做什么的正是对象
    public enum Language
    {
        English, German, Spanish
    }
    
    public class SpeakerFactory
    {
        public static ISpeaker CreateSpeaker(Language language)
        {
            switch (language)
            {
                case Language.English:
                    return new EnglishSpeaker();
                case Language.German:
                    return new GermanSpeaker();
                case Language.Spanish:
                    return new SpanishSpeaker();
                default:
                    throw new ApplicationException("No speaker can speak such language");
            }
        }
    }
    
    [STAThread]
    static void Main()
    {
        //This is your client code.
        ISpeaker speaker = SpeakerFactory.CreateSpeaker(Language.English);
        speaker.Speak();
        Console.ReadLine();
    }
    
    public interface ISpeaker
    {
        void Speak();
    }
    
    public class EnglishSpeaker : ISpeaker
    {
        public EnglishSpeaker() { }
    
        #region ISpeaker Members
    
        public void Speak()
        {
            Console.WriteLine("I speak English.");
        }
    
        #endregion
    }
    
    public class GermanSpeaker : ISpeaker
    {
        public GermanSpeaker() { }
    
        #region ISpeaker Members
    
        public void Speak()
        {
            Console.WriteLine("I speak German.");
        }
    
        #endregion
    }
    
    public class SpanishSpeaker : ISpeaker
    {
        public SpanishSpeaker() { }
    
        #region ISpeaker Members
    
        public void Speak()
        {
            Console.WriteLine("I speak Spanish.");
        }
    
        #endregion
    }