[英]Polymorphism in java: Why do we set parent reference to child object?
我想了解设置对子对象的父引用的用例。 示例:Dog 类扩展了 Animal 类。 (没有接口,请注意)我通常会像这样创建一个 Dog 对象:
Dog obj = new Dog();
现在,由于 Dog 是 Animal 的子类,它已经可以访问 Animal 的所有方法和变量。 那么,这有什么区别:
Animal obj = new Dog();
请提供适当的用例及其使用的代码片段。 请不要关于“多态”或“接口编码”的理论文章!
代码:
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");
}
}
两种情况的输出相同。 那么为什么我们要设置父对象对子对象的引用呢?
让我有时间码字。
List<String> list = new ArrayList<String>;
list.doThis();
list.doThat();
哦等等..我疯了。 我想使用LinkedList
而不是ArrayList
List<String> list = new LinkedList<String>;
list.doThis();
list.doThat();
是的,我只需要更改声明部分。 无需触及我的所有代码。 感谢编程接口和超类。
这是一个原则的实现,它说 -
编程到接口,而不是实现。
例如,如果您设计了一个方法来接受类型为Animal
的引用,那么将来您可以轻松地将 an= Cat
实现传递给它(当然前提是Cat
是Animal
的子类型。
意思是 -
public void doSomethingWithAnimal(Animal animal) {
// perform some action with/on animal
}
比 - 灵活得多
public void doSomethingWithAnimal(Dog d) {
// your code
}
因为对于第一种方法,您可以轻松地执行以下操作 -
doSomethingWithAnimal(new Cat());
如果您决定创建新的Cat
类型,继承自Animal
。
一般来说,你会知道java cast/oop的概念。
Dog
是一种Animal
因此您可以将其分配给动物。
但是您不能将Animal
分配给Dog
。 因为它可以是任何其他动物,如Cat
。 如果您确定对象是Dog
,则可以将其转换为Animal
。 如果Animal
是Dog
类型,那么您不能神奇地转换为Goat
。
尽管有一些很好的答案(在“meh”中),但似乎没有一个是您可以接受的。 也许它们过于理论化或包含您不感兴趣的细节。所以再试一次:
对于您描述的示例,这无关紧要。 如果你真的只有两行方法,比如
void doit()
{
Animal x = new Dog();
x.shout();
}
那么你也可以写
void doit()
{
Dog x = new Dog();
x.shout();
}
这不会有直接的缺点。
甚至可以概括这种说法:对于仅在本地使用的参考,这无关紧要。 当你在方法中声明引用,并且只在这个方法中使用这个引用,并且不将它传递给其他方法时,那么将它声明为Animal
而不是Dog
并没有直接的优势。 你可以两者兼而有之。
即使你对此不感兴趣,我也不能省略它:
...使用父类型是最佳实践的一部分:
您应该始终使用足以满足您想要做的事情的最不具体的类型
这有多种技术原因,涉及抽象、泛化、灵活性、多态性的应用,甚至可以将其称为一种“类型卫生”。
并且这明确也指引用仅在本地使用的情况:如果您不想调用特定于类型Dog
,而只想调用Animal
类中的方法,那么您应该清楚这一点通过将变量声明为Animal
- 仅仅是因为这是您需要的最不具体的类型。 所以使用类型的间接好处Animal
在这些情况下-即很显然,下面的代码将只使用方法Animal
类,并没有任何的Dog
类。
人们可以继续并在此处进一步论证、用例示例和技术细节。 但是为此,您可以参考其他答案,或者一些中级或高级教科书和教程。
好的。 我想我得到了答案。
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..");
}
}
因此,当我们对子类对象使用父引用时,我们无法使用该对象访问子类中的任何特定方法(父类中不存在的方法)。
当您从这样一个简单的示例开始时,您看不到任何好处,因为您已将变量与它将保存的实际对象类型紧密耦合。
仅当您考虑方法声明中参数是方法实现所需的最不特定类型的方法声明时,多态性才会发挥作用:然后您可以使用任何子类型调用它,并且该方法将知道如何处理它,即使它没有实际对象类型的知识。 这就是类型的 Liskov 可替代性的本质。
所以想象你有一个方法
int getAge(Animal a) {
return Days.toYears(currentDate() - a.dateOfBirth());
}
该方法将适用于任何Animal
,甚至是您在定义方法后定义的那些。
但是,如果您碰巧理解了上述内容,请具体询问为什么要写
Animal a = new Dog();
那么它通常仍然是有道理的:您预先承诺不会提及实例的任何特定于狗的方面。 通常你会看到
List<String> strings = new ArrayList<>();
在这种情况下,我们知道其余代码不依赖于ArrayList
作为列表实现的特定选择。 这比上面描述的差异要小得多,但它结合了简洁、安全和定制,使其得以坚持。
这将是当您希望您正在编写的代码针对 Animal 接口而不是 Dog 实现工作时。 从长远来看,以这种方式创建对象会使您的代码更加健壮。
我经常使用:
List<Object> aList = new ArrayList<>();
这在定义类级别变量时很重要,因为即使稍后更改不重要的细节,您也希望整个对象都能工作。
看问题:-
Polymorphism in java: Why do we set parent reference to child object?
在如下方法中(工厂模式):-
public Animal doSomething(String str){
if(str.equals("dog")){
return new Dog();
}
else if(str.equals("cat")){
return new Cat();
}
else {
return new Animal();
}
}
您将获得一种 Animal 类型和 Dog 或 Cat 的实际对象,因此如果调用的方法在基类中被重写,则调用 Animal 的方法将调用在 Dog 或 Cat 的实际对象中重写的方法。 它为您提供了运行时的灵活性,可以根据实际对象和基类中的重写方法(如果有)来决定要运行的方法。
完整的例子如下:-
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
AnimalFactory 有一个返回 Animal 的 createAnimal 方法。 因为狗、狮子和马都是动物。 所以我们可以通过使用 Animal 返回类型来创建 Dog、Lion 和 Horse 对象。 我们使用 Animal 返回类型实现的是
Animal animal = new Dog();
Animal animal = new Lion();
Animal animal = new Horse();
如果没有 Animal 返回类型,这是不可能的。
如果我在 createAnimal 方法中使用返回类型为 Dog ,那么它不能返回 Lion 或 Horse 等。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.