简体   繁体   English

java中的多态性:为什么我们要设置对子对象的父引用?

[英]Polymorphism in java: Why do we set parent reference to child object?

I want to understand the use-case of setting a parent reference to a child object.我想了解设置对子对象的父引用的用例。 Example: Dog class extends Animal class.示例:Dog 类扩展了 Animal 类。 (No interfaces, mind it) I would normally create an object of Dog like this: (没有接口,请注意)我通常会像这样创建一个 Dog 对象:

Dog obj = new Dog();

Now, since Dog is a subclass of Animal it already has access to all of Animal's methods and variables.现在,由于 Dog 是 Animal 的子类,它已经可以访问 Animal 的所有方法和变量。 Then, what difference does this make:那么,这有什么区别:

Animal obj = new Dog(); 

Please provide a proper use-case with an code snippet of its use.请提供适当的用例及其使用的代码片段。 No theoretical articles about 'Polymorphism' or 'Coding to interfaces' please!请不要关于“多态”或“接口编码”的理论文章!

Code:代码:

public class Polymorphism {
    public static void main(String[] args){
        Animal obj1 = new Dog();
        Dog obj2 = new Dog();
        obj1.shout(); //output is bark..
        obj2.shout(); //output is bark..        
    }   
}

class Animal{
    public void shout(){
        System.out.println("Parent animal's shout");
    }       
}

class Dog extends Animal{
    public void shout(){
        System.out.println("bark..");
    }
}

class Lion extends Animal{
    public void shout(){
        System.out.println("roar..");
    }
}

class Horse extends Animal{
    public void shout(){
        System.out.println("neigh");
    }
}

Output is the same for both the cases.两种情况的输出相同。 Then why do we set parent reference to child object?那么为什么我们要设置父对象对子对象的引用呢?

Let me code some time.让我有时间码字。

List<String> list = new ArrayList<String>;
list.doThis();
list.doThat();

Oh wait ..I'm gone mad.哦等等..我疯了。 I want to use LinkedList instead of ArrayList我想使用LinkedList而不是ArrayList

List<String> list = new LinkedList<String>;
list.doThis();
list.doThat();

Yup, I have to change only declaration part.是的,我只需要更改声明部分。 No need to touch all of my code.无需触及我的所有代码。 Thanks to programming to interfaces and with super classes.感谢编程接口和超类。

This is an implementation of a principle which says -这是一个原则的实现,它说 -

Program to an interface, not to an implementation.编程到接口,而不是实现。

As an example, if you design a method to accept a reference of type Animal , then in future you can easily pass an= Cat implementation to it (provided of course that the Cat is a sub-type of Animal .例如,如果您设计了一个方法来接受类型为Animal的引用,那么将来您可以轻松地将 an= Cat实现传递给它(当然前提是CatAnimal的子类型。

Which means -意思是 -

public void doSomethingWithAnimal(Animal animal) {
    // perform some action with/on animal
}

is much more flexible than -比 - 灵活得多

public void doSomethingWithAnimal(Dog d) {
    // your code
}

because for the first method, you can easily do something like -因为对于第一种方法,您可以轻松地执行以下操作 -

doSomethingWithAnimal(new Cat());

if you ever decide to create new Cat type, inheriting from Animal .如果您决定创建新的Cat类型,继承自Animal

Think generally, you will know java casting/oop concept.一般来说,你会知道java cast/oop的概念。

Dog is a type of Animal so you can assign it to an animal. Dog是一种Animal因此您可以将其分配给动物。

But you can't assign Animal to a Dog .但是您不能将Animal分配给Dog Because it can be any other animal like Cat .因为它可以是任何其他动物,如Cat If you are sure the object is Dog , you can caste that to Animal .如果您确定对象是Dog ,则可以将其转换为Animal If the Animal is of type Dog then you cannot magically cast to a Goat .如果AnimalDog类型,那么您不能神奇地转换为Goat

Although there are some good answers (among the "meh" ones), it seems like none was acceptable for you.尽管有一些很好的答案(在“meh”中),但似乎没有一个是您可以接受的。 Maybe they are too theoretical or contain details that you are not interested in. So another try:也许它们过于理论化或包含您不感兴趣的细节。所以再试一次:


For the example that you described, it does not matter.对于您描述的示例,这无关紧要。 If you really only have a two-line method like如果你真的只有两行方法,比如

void doit()
{
    Animal x = new Dog();
    x.shout();
}

then you could also have written那么你也可以写

void doit()
{
    Dog x = new Dog();
    x.shout();
}

and this would not have a direct disadvantage.不会有直接的缺点。


One could even generalize this statement: For a reference that is only used locally , it does not matter.甚至可以概括这种说法:对于仅在本地使用的参考,这无关紧要。 When you declare the reference in the method, and only use this reference in this method, and do not pass it to other methods, then there is no direct advantage in declaring it as Animal instead of as Dog .当你在方法中声明引用,并且在这个方法中使用这个引用,并且将它传递给其他方法时,那么将它声明为Animal而不是Dog并没有直接的优势。 You can to both.你可以两者兼而有之。

But...但...

even if you are not interested in this, I can't omit it:即使你对此不感兴趣,我也不能省略它:

... using the parent type is part of a best practice: ...使用父类型是最佳实践的一部分:

You should always use the least specific type that is sufficient for what you want to do您应该始终使用足以满足您想要做的事情的最不具体的类型

This has various technical reasons, regarding abstraction, generalization, flexibility, the application of polymorphism, and one could even go so far to call it a sort of "type hygiene".这有多种技术原因,涉及抽象、泛化、灵活性、多态性的应用,甚至可以将其称为一种“类型卫生”。

And this explicitly also refers to the case where the reference is only used locally: If you don't want to call methods that are specific for the type Dog , but only want to call methods from the Animal class, then you should make this clear by declaring the variable as an Animal - simply because that's the least specific type that you need.并且这明确指引用仅在本地使用的情况:如果您不想调用特定于类型Dog ,而只想调用Animal类中的方法,那么您应该清楚这一点通过将变量声明为Animal - 仅仅是因为这是您需要的最不具体的类型。 So there is an indirect advantage of using the type Animal in these cases - namely that it is clear that the following code will only use methods of the Animal class, and none of the Dog class.所以使用类型的间接好处Animal在这些情况下-即很显然,下面的代码将只使用方法Animal类,并没有任何Dog类。

One could continue and go very far with further justifications, use case examples and technical details here.人们可以继续并在此处进一步论证、用例示例和技术细节。 But for this, you may refer to the other answers, or some intermediate or advanced texbooks and tutorials.但是为此,您可以参考其他答案,或者一些中级或高级教科书和教程。

Okay.好的。 I think I got my answer.我想我得到了答案。

public class Polymorphism {
    public static void main(String[] args){
        Animal obj1 = new Horse();
        Horse obj2 = new Horse();

        obj1.shout();    //output is neigh..
        obj2.shout();    //output is neigh..
        obj1.winRaces(); /*But this is not allowed and throws compile time error, 
                           even though the object is of Animal type.*/ 
    }   
}

class Animal{
    public void shout(){
        System.out.println("Parent animal's shout");
    }       
}

class Horse extends Animal{
    public void shout(){
        System.out.println("neigh..");
    }
    public void winRaces(){
        System.out.println("won race..");
    }
}

So, when we use parent reference for child class object, we cannot access any specific methods in child class (that are not present in parent class) using that object.因此,当我们对子类对象使用父引用时,我们无法使用该对象访问子类中的任何特定方法(父类中不存在的方法)。

When you start with such a simple example, you can't see any benefits because you have tightly coupled the variable to the actual type of object it will hold.当您从这样一个简单的示例开始时,您看不到任何好处,因为您已将变量与它将保存的实际对象类型紧密耦合。

Polymorphism comes into its own only when you consider method declarations where the parameter is of the least specific type needed by the method's implementation: then you can call it with any subtype and the method will know what to do with it, even though it has no knowledge of the actual object type.仅当您考虑方法声明中参数是方法实现所需的最不特定类型的方法声明时,多态性才会发挥作用:然后您可以使用任何子类型调用它,并且该方法将知道如何处理它,即使它没有实际对象类型的知识。 That's the essence of Liskov substitutability of types.这就是类型的 Liskov 可替代性的本质。

So imagine you have a method所以想象你有一个方法

int getAge(Animal a) {
   return Days.toYears(currentDate() - a.dateOfBirth());
}

The method will work against any Animal , even those you defined after defining the method.该方法将适用于任何Animal ,甚至是您在定义方法后定义的那些。


But, if you happen to understand the above, yet ask specifically why one would write但是,如果您碰巧理解了上述内容,请具体询问为什么要写

Animal a = new Dog();

then it still often makes sense: you promise up-front that you won't refer to any dog-specific aspects of the instance.那么它通常仍然是有道理的:您预先承诺不会提及实例的任何特定于狗的方面。 Typically you'll see通常你会看到

List<String> strings = new ArrayList<>();

and in this case we know that the rest of the code doesn't rely on the specific choice of ArrayList as list implementation.在这种情况下,我们知道其余代码不依赖于ArrayList作为列表实现的特定选择。 This is a much smaller difference than the one decribed above, but it's a combination of brevity, safety, and custom which makes it stick.这比上面描述的差异要小得多,但它结合了简洁、安全和定制,使其得以坚持。

This would be when you want the code that you're writing to work against the Animal interface instead of the Dog implementation.这将是当您希望您正在编写的代码针对 Animal 接口而不是 Dog 实现工作时。 Creating an object in this way makes your code more robust in the long term.从长远来看,以这种方式创建对象会使您的代码更加健壮。

I frequently use:我经常使用:

List<Object> aList = new ArrayList<>();

This is important when defining class level variables, because you want your whole object to work even if you change an unimportant detail later.这在定义类级别变量时很重要,因为即使稍后更改不重要的细节,您也希望整个对象都能工作。

Looking at the question:-看问题:-

Polymorphism in java: Why do we set parent reference to child object?

In a method like below(Factory Pattern):-在如下方法中(工厂模式):-

public Animal doSomething(String str){
if(str.equals("dog")){
    return new Dog();
    }
else if(str.equals("cat")){
    return new Cat();
    }
else {
    return new Animal();
    }
}

You get a type of Animal and actual object of either Dog or Cat so calling a method of Animal will call the method overridden in actual Object of either Dog or Cat if the called method is overridden in base class.您将获得一种 Animal 类型和 Dog 或 Cat 的实际对象,因此如果调用的方法在基类中被重写,则调用 Animal 的方法将调用在 Dog 或 Cat 的实际对象中重写的方法。 It provides you with the flexibility at run time to decide which method to run depending on the actual object and overridden method in base class if any.它为您提供了运行时的灵活性,可以根据实际对象和基类中的重写方法(如果有)来决定要运行的方法。

The complete example is as under :-完整的例子如下:-

package com.test.factory;

public class Animal{
    public void shout(){
        System.out.println("Parent animal's shout");
    }       
}

package com.test.factory;

public class Dog extends Animal{

    @Override
    public void shout(){
        System.out.println("bark..");
    }
}

package com.test.factory;

public class Horse extends Animal{

    @Override
    public void shout(){
        System.out.println("neigh");
    }
}

package com.test.factory;

public class Lion extends Animal{

    @Override
    public void shout(){
        System.out.println("roar..");
    }
}

    package com.test.factory;

    public class AnimalFactory {

        public Animal createAnimal(String str){

            if(str.equals("dog")){
                return new Dog();
                }
            else if (str.equals("horse")){
                return new Horse();
                }

            else if(str.equals("lion")){
                return new Lion();
                }
            else{
                return new Animal();
            }
        }

    }

    package com.test.factory;


  package com.test.factory;


public class Polymorphism {
    public static void main(String[] args){

        AnimalFactory factory = new AnimalFactory();
        Animal animal = factory.createAnimal("dog");
        animal.shout();
        animal = factory.createAnimal("lion");
        animal.shout();
        animal = factory.createAnimal("horse");
        animal.shout();
        animal = factory.createAnimal("Animal");
        animal.shout();

    }   
}


    Output is :-
    bark..
    roar..
    neigh
    Parent animal's shout

The AnimalFactory has a createAnimal method which returns Animal. AnimalFactory 有一个返回 Animal 的 createAnimal 方法。 Since Dog, Lion and Horse are all animals.因为狗、狮子和马都是动物。 So we are able to create Dog, Lion and Horse objects by using Animal return type.所以我们可以通过使用 Animal 返回类型来创建 Dog、Lion 和 Horse 对象。 What we achieved using Animal return type is我们使用 Animal 返回类型实现的是

Animal animal = new Dog();
Animal animal = new Lion();
Animal animal = new Horse();

which is not possible without the Animal return type.如果没有 Animal 返回类型,这是不可能的。

If I use return type as Dog in createAnimal method then it cannot return Lion or Horse and like wise.如果我在 createAnimal 方法中使用返回类型为 Dog ,那么它不能返回 Lion 或 Horse 等。

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

相关问题 为什么我们在 Java 中为子对象分配父引用? - Why do we assign a parent reference to the child object in Java? 为什么我们使用父类引用在Java中进行覆盖? - Why do we use Parent Class reference for overriding in Java? 在Java中使用父多态单独引用孩子吗? - Is referencing a child with parent, alone polymorphism in Java? 为什么我们将对象存储在接口引用中? - Why do we store object in an interface reference? 为什么我们需要java中的弱引用 - Why do we need weak reference in java 当使用Java中的多态性从父类打印子私有字段时,为什么子私有字段为null? - Why child private field is null when printing it from parent class using polymorphism in java? 为什么在父类中必须强制有一个子方法以引用父类的引用来调用它,该父类引用子类的对象? - Why is it mandatory to have a child method in Parent class to call it with a reference of parent class which is referring to an object of child class? Java多态-从父映射获取子命令 - Java polymorphism - getting child command from parent map Java解决父类和子类对象的多态性 - Polymorphism in java resolving Parent class and Child class objects 为什么我们能够在Java中将数组分配给Object类型的引用? - Why are we able to assign arrays to reference of type Object in Java?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM