是否可以在超类对象上调用子类的方法?

Posted

技术标签:

【中文标题】是否可以在超类对象上调用子类的方法?【英文标题】:Is it possible to call subclasses' methods on a superclass object? 【发布时间】:2010-10-28 06:54:04 【问题描述】:

Animal 是 Dog 的超类 Dog 有一个叫 bark 的方法

public void bark()

    System.out.println("woof");

考虑以下几点:

Animal a = new Dog();
if (a instanceof Dog)
    a.bark();

会发生什么?

    不允许分配 允许调用 bark 并在运行时打印“woof” 允许调用 bark,但不打印任何内容 对 bark 的调用导致编译时错误 对 bark 的调用导致运行时错误

我说的是 2,因为我们正在检查对象是否是狗;因为 dog 是其中包含 bark 方法的类,如果是,则我们调用它,它将打印出来:s

我的理解正确吗?

【问题讨论】:

建议将这里的问题改为“超类是否有其子类的方法?”或类似的。 诀窍是您正在检查以确保它是一只狗,但编译器没有建立 a.bark 仅在 a 是狗时才被调用的逻辑连接。为了调用 bark() 的目的,您必须明确告诉编译器将 a 视为 Dog,如下面的 Simon622 所述。 顺便说一句,这就是为什么许多人更喜欢“鸭式打字”的原因。例如,在 Python 中,等效代码甚至不需要检查 a 是否为 Dog;只要a 在到达该行时有一个bark 方法,它就可以工作。 幸运的是只有 1 分,如果问题是“超类有其子类的方法吗?”我会得到标记的:@ 大声笑无论如何我可以在这里扩展问题,以便我可以检查更多 回复:鸭子打字--错误的答案。如果动物可以发出声音,makeNoise() 应该在动物基类中。您几乎不必使用强制转换。如果只有一些动物会发出噪音,那应该是 dog 扩展的子类(或者该方法应该抛出异常或返回 null)。好处是,如果你的类设计得当,你很少需要强制转换,所以如果你发现鸭式打字真的有用,请检查你的设计技巧。 【参考方案1】:

这不会编译,因为 Animal 没有名为 bark 的方法。这样想,所有的狗都是动物,但不是所有的动物都是狗。所有的狗都会吠叫,但并非所有的动物都会吠叫。

【讨论】:

【参考方案2】:

否 - 答案是;

4) 对 bark 的调用导致编译时错误

bark 方法未定义为分配类型 Animal 上的方法,因此将导致编译时问题;这可以通过强制转换来解决;

((Dog)a).bark();

【讨论】:

【参考方案3】:

它是 4。你不能要求一个通用的动物——这就是你的代码所说的 a ——吠叫。因为你可以很容易地说

Animal a = new Cat();

而树皮线没有办法知道你没有。

【讨论】:

【参考方案4】:

关键在下面一行:

Animal a = new Dog();

虽然创建了Dog 的新实例,但它的引用是由a 声明的Animal 类型。因此,对a 的任何引用都会使new Dog 被视为Animal

因此,除非Animalbark 方法,否则以下行将导致编译器错误:

a.bark();

即使a 被测试以查看它是否是Dog 的一个实例,并且a instanceof Dog 将实际返回true,变量a 仍然属于Animal 类型,所以if 语句中的块仍将 a 处理为 Animal

这是statically-typed languages 的一个功能,其中变量被提前分配了一个类型,并在编译时检查以查看类型是否匹配。如果此代码是在 dynamically-typed language 上执行的,在运行时检查类型,则可能允许执行以下操作:

var a = new Dog();
if (a instanceof Dog)
    a.bark();

a.bark() 保证仅在实例为Dog 时执行,因此对bark 的调用将始终有效。但是,Java 是一种静态类型的语言,所以这种类型的代码是不允许的。

【讨论】:

+1 用于添加关于静态类型语言与动态类型语言的解释。 如果两个类都有 bark() 方法,会执行哪一个。 @Rahul Kadukar:如果“animaldog 的超类”,并且两个类都有 bark() 方法,然后将 bark() 覆盖为子类 dog 中的 bark() 方法。因此,a.bark() 将执行 dog 类中的 bark() 方法。【参考方案5】:

在Head First Java 中,他们将电视遥控器很好地类比为参考,而您的电视作为参考指向的对象。如果你的遥控器只有开、关、频道上下以及音量上下的按钮(方法),那么你的电视有什么很酷的功能并不重要。你仍然只能从遥控器上做那几件基本的事情。您无法将电视静音,例如,如果您的遥控器没有静音按钮。

Animal 参考只知道 Animal 方法。不管底层对象有什么其他方法,你都不能从 Animal 引用中访问它们。

【讨论】:

【参考方案6】:

“我说 2,因为我们正在检查对象是否是狗;因为 dog 是其中包含 bark 方法的类,如果是,那么我们调用它,它将打印出:s”

您的理由是正确的,但事实并非如此。

Java 是一种静态类型语言,这意味着,对象可能响应的方法的有效性在编译时进行验证。

你可能会认为支票:

if( a instanceof Dog ) 

会,但实际上不会。编译器所做的是检查声明类型的“接口”(在这种情况下为 Animal )。 “接口”由 Animal 类上声明的方法组成。

如果 bark() 方法没有在超类 Animal 中定义,编译器会说:“嘿,这行不通”。

这很有帮助,因为“有时”我们在编码时会打错字(例如输入 barck())

如果编译器没有对此发出警告,您将不得不在“运行时”找到它,而且并不总是带有明确的消息(例如 IE 中的 javascript 会说“意外对象”之类的内容)

不过,像 java 这样的静态类型语言允许我们强制调用。在这种情况下,它使用“cast”运算符 ()

这样

1. Animal a = new Dog();
2.  if (a instanceof Dog)
3.     Dog imADog = ( Dog ) a;
4.     imADog.bark();
5. 

在第 3 行中,您正在“强制转换”为 Dog 类型,因此编译器可能会检查 bark 是否是有效消息。

这是对编译器的指令,说“嘿,我是这里的程序员,我知道我在做什么”。并且编译器检查,好的,狗,可以接收到消息 bark(),继续。然而,如果在运行时动物不是狗,则会引发运行时异常。

演员表也可以缩写为:

if( a instanceof Dog ) 
   ((Dog)a).bark();  

这将运行。

所以,正确答案是4:对 bark 的调用导致编译时错误

我希望这会有所帮助。

【讨论】:

【参考方案7】:

仅供参考,这不是一个好的设计。

几乎任何时候你都有这种形式的代码:

if (x instanceof SomeClass)

   x.SomeMethod();

你在滥用类型系统。这不是使用类的方式,也不是编写可维护的面向对象代码的方式。它很脆。很复杂。这很糟糕。

您可以在基类中创建模板方法,但它们必须调用存在于基类中并在子类中被覆盖的方法。

【讨论】:

【参考方案8】:

如果想法是从超类对象打印子类方法,这将起作用:

而不是Animal a = new Dog(); if (a instanceof Dog) a.bark(); 改为

Animal a = new Dog();

if (a instanceof Dog) 
    Dog d = (Dog) a; 
    d.bark();
  

这会将超类转换回子类并打印它。 虽然它的设计很糟糕,但它是动态知道它指向哪个子类对象的一种方法。

【讨论】:

或者,如果你必须这样做,你可以这样做if(a instanceof Dog) ((Dog) a).bark(); 【参考方案9】:

在 java(我只知道的语言)中,您可以创建一个空方法并在超类中调用它。然后你可以在子类中覆盖它来做任何你想做的事情。这样超类调用其子类的方法。

public class Test 
    public static void main(String[] args)
        Snake S = new Snake();
        Dog d = new Dog();
    



class Animal //Super Class
    public Animal()
        bark(); //calls bark when a new animal is created
    
    public void bark()
        System.out.println("this animal can't bark");
    




class Dog extends Animal //Subclass 1
    @Override
    public void bark()
        System.out.println("Woof");
    




class Snake extends Animal//Subclass 2
    public void tss()
    

此代码调用 Snake 的对象,然后调用 Dog 的对象。它将这个写入控制台:

this animal can't bark
Woof

Snake 没有任何 bark 方法,因此调用了超类的方法。它将第一行写入控制台。 Dog 有一个 bark 方法,所以超类调用它。它将第二行写入控制台。

【讨论】:

以上是关于是否可以在超类对象上调用子类的方法?的主要内容,如果未能解决你的问题,请参考以下文章

Java超类对象引用访问子类对象

在Python 3中,超类可以多态地调用子类的构造函数

马凯军201771010116《面向对象程序设计(java)》第七周学习总结

Java:在超类方法签名中返回子类

调用对象方法传递超类期望子类

Java多态如何调用子类对象的超类方法