9

我知道这个问题被问了很多,但在我看来,通常的答案远不能令人满意。

给定以下类层次结构:

class SuperClass{}
class SubClass extends SuperClass{}

为什么人们使用这种模式来实例化子类:

SuperClass instance = new SubClass();

而不是这个:

SubClass instance = new SubClass();

现在,我看到的通常答案是,这是为了将instance作为参数发送到需要 SuperClass 实例的方法,如下所示:

void aFunction(SuperClass param){}

//somewhere else in the code...
...
aFunction(instance);
...

但是我可以将 SubClass 的实例发送到 aFunction 而不管保存它的变量的类型!这意味着以下代码将编译并运行而没有错误(假设之前提供的 aFunction 定义):

SubClass instance = new SubClass();
aFunction(instance);

事实上,AFAIK 变量类型在运行时毫无意义。它们仅供编译器使用!

将变量定义为 SuperClass 的另一个可能原因是它有几个不同的子类,并且该变量应该在运行时切换它对其中几个的引用,但例如我只在类中看到了这种情况(不是 super,不是 sub.只是上课)。绝对不足以要求通用模式......

4

5 回答 5

8

这种类型编码的主要论点是因为Liskov 替换原则,它指出如果X是 type 的子类型T,那么任何实例都T应该能够被替换掉X

这样做的好处很简单。假设我们有一个具有属性文件的程序,如下所示:

mode="Run"

您的程序如下所示:

public void Program
{
    public Mode mode;

    public static void main(String[] args)
    {
        mode = Config.getMode();
        mode.run();
    }
}

简单地说,这个程序将使用配置文件来定义这个程序启动的模式。在Config类中,getMode()可能如下所示:

public Mode getMode()
{
    String type = getProperty("mode"); // Now equals "Run" in our example.

    switch(type)
    {
       case "Run": return new RunMode();
       case "Halt": return new HaltMode();  
    }
}

为什么这不起作用

现在,因为您有 type 的引用,您可以通过简单地更改属性Mode的值来完全更改程序的功能。mode如果您有public RunMode mode,您将无法使用此类功能。

为什么这是一件好事

这种模式之所以流行,是因为它为程序打开了可扩展性。这意味着如果作者希望实现这种功能,这种类型的所需功能可以通过最少的更改来实现。我的意思是,来吧。您更改配置文件中的一个单词并完全改变程序流程,而无需编辑任何一行代码。这是可取的。

于 2013-10-01T10:12:19.927 回答
3

在许多情况下,这并不重要,但被认为是好的风格。您将提供给参考用户的信息限制为必要的信息,即它是 type 的实例SuperClass。变量是否引用类型SuperClassSubClass.

更新

对于从未用作参数等的局部变量也是如此。正如我所说,这通常无关紧要,但被认为是好的风格,因为您以后可能会更改变量以保存参数或超类型的另一个子类型. 在这种情况下,如果您首先使用子类型,那么您的进一步代码(在该单一范围内,例如方法)可能会意外地依赖于一个特定子类型的 API,并且将变量更改为另一种类型可能会破坏您的代码。

我将扩展 Chris 的示例:

考虑你有以下几点:

RunMode mode = new RunMode();

...

您现在可能依赖modeRunMode.

但是,稍后您可能希望将该行更改为:

RunMode mode = Config.getMode(); //breaks

糟糕,编译不出来。好的,让我们改变它。

Mode mode = Config.getMode(); 

该行现在可以编译,但您的进一步代码可能会中断,因为您不小心依赖于mode成为RunMode. 请注意,它可能会编译,但可能会在运行时中断或破坏您的逻辑。

于 2013-10-01T10:08:40.373 回答
2

SuperClass instance = new SubClass1()

几行之后,你可以做instance = new SubClass2();

但如果你写,SubClass1 instance = new SubClass1();

几行之后,你不能做instance = new SubClass2()

于 2013-10-01T10:45:44.523 回答
1

它被称为多态,它是对子类对象的超类引用。

In fact, AFAIK variable types are meaningless at runtime. They are used 
only by the compiler!

不知道你从哪里读到的。在编译时,编译器只知道引用类型的类(如您所说,在多态的情况下是超类)。在运行时,java 知道 Object(.getClass()) 的实际类型。在编译时,java 编译器只检查调用的方法定义是否在引用类型的类中。调用哪个方法(函数重载)是在运行时根据对象的实际类型确定的。

Why polymorphism?

好吧谷歌找到更多,但这里是一个例子。你有一个通用的方法draw(Shape s)。现在 shape 可以是 a Rectangle, a Circleany CustomShape。如果您不在draw()方法中使用形状引用,您将不得不为每种形状的(子类)类型创建不同的方法。

于 2013-10-01T10:08:26.087 回答
0

这是从设计的角度来看,您将拥有一个超类,并且可以有多个子类,您希望在其中扩展功能。

必须编写子类的实现者只需要关注要覆盖的方法

于 2013-10-01T10:05:07.247 回答