使用超类类型作为子类实例

时间:2013-10-01 10:01:03

标签: java inheritance casting polymorphism type-conversion

我知道这个问题已被提出很多问题,但在我看来,通常的答案远非令人满意。

给出以下类层次结构:

class SuperClass{}
class SubClass extends SuperClass{}

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

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的另一个可能的原因是,如果它有几个不同的子类,并且该变量应该在运行时将它的引用切换为其中的几个,但我举例说,这只发生在类中(不是超级,不要只是上课。绝对不足以要求一般模式......

5 个答案:

答案 0 :(得分:8)

此类编码的主要论据是因为Liskov Substituion Principle,其中指出如果XT类型的子类型,那么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();  
    }
}

为什么这不起作用

现在,因为您有Mode类型的引用,所以只需更改mode属性的值即可完全更改程序的功能。如果您有public RunMode mode,则无法使用此类功能。

为什么这是一件好事

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

答案 1 :(得分:3)

在许多情况下,它并不重要,但被认为是好的风格。 您将提供给用户的信息限制为必要的参考,即它是SuperClass类型的实例。它不会(也不应该)关注变量是否引用SuperClassSubClass类型的对象。

<强>更新

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

我将扩展克里斯的例子:

考虑您有以下内容:

RunMode mode = new RunMode();

...

您现在可能依赖于modeRunMode

这一事实

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

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

糟糕,无法编译。好的,让我们改变它。

Mode mode = Config.getMode(); 

该行现在可以编译,但是您的其他代码可能会中断,因为您意外地依赖mode作为RunMode的实例。请注意,它可能会编译,但可能会在运行时中断或使您的逻辑失效。

答案 2 :(得分: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)。现在形状可以是RectangleCircle任意CustomShape。如果你不在draw()方法中使用Shape引用,则必须为每种类型的(子类)形状创建不同的方法。

答案 3 :(得分:1)

SuperClass instance = new SubClass1()

在某些行之后,您可以执行instance = new SubClass2();

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

在某些行之后,您无法执行instance = new SubClass2()

答案 4 :(得分:0)

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

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