简体   繁体   English

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

[英]Using superclass type for subclass instance

I know this question has been asked a lot, but the usual answers are far from satisfying in my view. 我知道这个问题已被问到很多,但在我看来,通常的答案远非令人满意。

given the following class hierarchy: 给定以下类层次结构:

class SuperClass{}
class SubClass extends SuperClass{}

why does people use this pattern to instantiate SubClass: 为什么人们使用这种模式来实例化SubClass:

SuperClass instance = new SubClass();

instead of this one: 而不是这一个:

SubClass instance = new SubClass();

Now, the usual answer I see is that this is in order to send instance as an argument to a method that requires an instance of SuperClass like here: 现在,我看到的通常答案是,这是为了将instance作为参数发送到需要SuperClass实例的方法,如下所示:

void aFunction(SuperClass param){}

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

But I can send an instance of SubClass to aFunction regardless of the type of variable that held it! 但我可以将一个SubClass实例发送到aFunction,而不管它持有它的变量类型! meaning the following code will compile and run with no errors (assuming the previously provided definition of aFunction): 意味着以下代码将编译并运行而没有错误(假设先前提供的aFunction定义):

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

In fact, AFAIK variable types are meaningless at runtime. 实际上,AFAIK变量类型在运行时是没有意义的。 They are used only by the compiler! 它们仅由编译器使用!

Another possible reason to define a variable as SuperClass would be if it had several different subclasses and the variable is supposed to switch it's reference to several of them at runtime, but I for example only saw this happen in class (not super, not sub. just class). 将变量定义为SuperClass的另一个可能原因是,如果它有几个不同的子类,并且该变量应该在运行时将它的引用切换为其中的几个,但我举例说,这只发生在类中(不是超级,而不是子类)。只是上课)。 Definitly not sufficient to require a general pattern... 绝对不足以要求一般模式......

The main argument for this type of coding is because of the Liskov Substituion Principle , which states that if X is a subtype of type T , then any instance of T should be able to be swapped out with X . 对于这种类型的编码的主要论据是因为的里氏Substituion原理 ,其中指出,如果X是类型的子类型T ,然后的任何实例T应该能够与被换出X

The advantage of this is simple. 这样做的好处很简单。 Let's say we've got a program that has a properties file, that looks like this: 假设我们有一个包含属性文件的程序,如下所示:

mode="Run"

And your program looks like this: 你的程序看起来像这样:

public void Program
{
    public Mode mode;

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

So briefly, this program is going to use the config file to define the mode this program is going to boot up in. In the Config class, getMode() might look like this: 简而言之,这个程序将使用配置文件来定义该程序将要启动的模式。在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();  
    }
}

Why this wouldn't work otherwise 为什么这不起作用

Now, because you have a reference of type Mode , you can completely change the functionality of your program with simply changing the value of the mode property. 现在,因为您有类型为Mode的引用,所以只需更改mode属性的值即可完全更改程序的功能。 If you had public RunMode mode , you would not be able to use this type of functionality. 如果您使用public RunMode mode ,则无法使用此类功能。

Why this is a good thing 为什么这是一件好事

This pattern has caught on so well because it opens programs up for extensibility. 这种模式已经很好地发挥作用,因为它为可扩展性打开了程序。 It means that this type of desirable functionality is possible with the smallest amount of changes, should the author desire to implement this kind of functionality. 这意味着如果作者希望实现这种功能,那么这种类型的所需功能可以通过最少量的更改实现。 And I mean, come on. 我的意思是,来吧。 You change one word in a config file and completely alter the program flow, without editing a single line of code. 您可以更改配置文件中的一个单词并完全更改程序流,而无需编辑任何一行代码。 That is desirable. 这是可取的。

In many cases it doesn't really matter but is considered good style. 在许多情况下,它并不重要,但被认为是好的风格。 You limit the information provided to users of the reference to what is nessary, ie that it is an instance of type SuperClass . 您将提供给用户的信息限制为必要的参考,即它是SuperClass类型的实例。 It doesn't (and shouldn't) matter whether the variable references an object of type SuperClass or SubClass . 它不会(也不应该)关注变量是否引用SuperClassSubClass类型的对象。

Update : 更新

This also is true for local variables that are never used as a parameter etc. As I said, it often doesn't matter but is considered good style because you might later change the variable to hold a parameter or another sub type of the super type. 对于从未用作参数等的局部变量也是如此。正如我所说,它通常无关紧要但被认为是好的样式,因为您可能稍后更改变量以保存参数或超类型的另一个子类型。 In that case, if you used the sub type first, your further code (in that single scope, eg method) might accidentially rely on the API of one specific sub type and changing the variable to hold another type might break your code. 在这种情况下,如果您首先使用子类型,那么您的其他代码(在该单一范围内,例如方法) 可能会依赖于某个特定子类型的API,并且更改该变量以保留其他类型可能会破坏您的代码。

I'll expand on Chris' example: 我将扩展克里斯的例子:

Consider you have the following: 考虑您有以下内容:

RunMode mode = new RunMode();

...

You might now rely on the fact that mode is a RunMode . 您现在可能依赖于modeRunMode的事实。

However, later you might want to change that line to: 但是,稍后您可能希望将该行更改为:

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

Oops, that doesn't compile. 哎呀,不编译。 Ok, let's change that. 好的,让我们改变它。

Mode mode = Config.getMode(); 

That line would compile now, but your further code might break, because you accidentially relied to mode being an instance of RunMode . 该行现在将编译,但您的其他代码可能会中断,因为您意外地依赖于mode作为RunMode的实例。 Note that it might compile but could break at runtime or screw your logic. 请注意,它可能会编译,但可能会在运行时中断或拧紧您的逻辑。

It is called polymorphis and it is superclass reference to a subclass object. 它被称为多态,它是对子类对象的超类引用。

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

Not sure where you read this from. 不知道你从哪里读到这个。 At compile time compiler only know the class of the reference type(so super class in case of polymorphism as you have stated). 在编译时,编译器只知道引用类型的类(所以在多态性的情况下如此所说的超类)。 At runtime java knows the actual type of Object(.getClass()). 在运行时,java知道Object的实际类型(.getClass())。 At compile time java compiler only checks if the invoked method definition is in the class of reference type. 在编译时,java编译器仅检查调用的方法定义是否在引用类的类中。 Which method to invoke(function overloading) is determined at runtime based on the actual type of the object. 调用哪个方法(函数重载)是在运行时根据对象的实际类型确定的。

Why polymorphism?

Well google to find more but here is an example. 好吧谷歌找到更多,但这里是一个例子。 You have a common method draw(Shape s) . 你有一个常见的方法draw(Shape s) Now shape can be a Rectangle , a Circle any CustomShape . 现在shape可以是RectangleCircle可以是任何CustomShape If you dont use Shape reference in draw() method you will have to create different methods for each type of(subclasses) of shape. 如果你不在draw()方法中使用Shape引用,则必须为每种类型的(子类)形状创建不同的方法。

SuperClass instance = new SubClass1()

after some lines, you may do instance = new SubClass2(); 在某些行之后,您可以执行instance = new SubClass2();

But if you write, SubClass1 instance = new SubClass1() ; 但是如果你写, SubClass1 instance = new SubClass1() ;

after some lines, you can't do instance = new SubClass2() 在一些行之后,你不能做instance = new SubClass2()

This is from a design point of view, you will have one super class and there can be multiple subclasses where in you want to extend the functionality. 从设计的角度来看,您将拥有一个超类,并且可以有多个子类,您希望在其中扩展功能。

An implementer who will have to write a subclass need only to focus on which methods to override 必须编写子类的实现者只需关注要覆盖的方法

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM