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

18

动物是Dog的超类,而Dog有一个叫做bark的方法

public void bark()
{
    System.out.println("woof");
}

考虑以下内容:

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

会发生什么?

  1. 赋值不被允许。
  2. 调用bark是被允许的,"woof"将在运行时打印出来。
  3. 调用bark是被允许的,但什么都不会被打印出来。
  4. 对bark的调用导致编译时错误。
  5. 对bark的调用导致运行时错误。

我说2应该是正确的,因为我们正在检查对象是否是一个狗; 因为狗是具有吠叫方法的类,如果它是,则调用它将打印出:s

这里我的理解正确吗?


建议将此问题更改为“超类是否具有其子类的方法?”或类似问题。 - Tetsujin no Oni
1
诀窍在于您正在检查它是否为狗,但编译器没有逻辑连接,即只有在a是Dog时才会调用a.bark。您必须明确告诉编译器将a视为Dog,以便于bark()调用,就像Simon622在下面所说的那样。 - Andrew Coleson
1
顺便提一下,这就是为什么许多人更喜欢“鸭子类型”。在Python中,相应的代码甚至不需要检查a是否是Dog;只要当该行被执行时,a有一个bark方法,它就可以工作。 - Michael Myers
幸运的是,这只值1分哈哈天哪,如果问题写成“超类是否有其子类的方法?”我就可以得到分数了 :@无论如何,我能在这里扩展问题,这样我就可以检查更多 - roberto
4
关于鸭子类型的回复不太好。如果一个动物能发出声音,makeNoise() 应该在动物的基类中。你几乎永远不需要使用强制类型转换。如果只有一些动物能发出声音,那么这应该是一个子类,从中派生出狗(或者该方法应该抛出异常或返回 null)。优点是,如果你正确设计了你的类,你很少需要强制类型转换,所以如果你发现鸭子类型真的很有用,就检查一下你的设计技能。 - Bill K
显示剩余2条评论
9个回答

33

由于Animal没有名为bark的方法,因此这段代码无法编译。 可以这样想,所有的狗都是动物,但并非所有动物都是狗。 所有的狗会叫,但不是所有动物都会叫。


27

不,答案是:

4)调用bark会导致编译时错误。

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

((Dog)a).bark();

12
以下代码行包含关键信息:
Animal a = new Dog();

尽管创建了一个新的Dog实例,但它的引用是由声明为Animal类型的a所持有的。因此,对a的任何引用都会将new Dog处理为Animal
因此,除非Animal具有bark方法,否则以下行将导致编译错误:
a.bark();

即使对a进行了测试以查看它是否是Dog的实例,并且a instanceof Dog实际上将返回true,变量a仍然属于Animal类型,因此if语句中的块仍将a处理为Animal
这是静态类型语言的一个特性,其中变量预先分配了类型,并在编译时检查类型是否匹配。如果在动态类型语言上执行此代码,则可以允许以下内容:
var a = new Dog();
if (a instanceof Dog)
    a.bark();

a.bark()只有在实例为Dog时才能保证执行,因此对bark的调用总是有效的。然而,Java是一种静态类型语言,所以不允许这种类型的代码。


添加有关静态语言与动态语言的解释 +1。 - Grant Wagner
如果两个类都有bark()方法,那么哪一个会被执行? - Rahul Kadukar
1
如果“animaldog的超类”,并且两个类都有bark()方法,则bark()被覆盖为子类dog中的bark()方法。因此,a.bark()将执行dog类中的**bark()**方法。 - NoName

6
Head First Java中,他们使用了一个非常好的比喻来解释引用和你的电视作为引用指向的对象。如果你的遥控器只有开、关、频道上下和音量上下这些按钮(方法),那么你的电视有多酷的功能都无所谓。你仍然只能从遥控器上做这几个基本的事情。例如,如果你的遥控器没有静音按钮,你就不能将电视静音。
动物引用只知道动物方法。不管底层对象有什么其他方法,你都不能从动物引用访问它们。

4

如果想要从超类对象中打印子类方法,可以这样实现:

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(); } - GreenMatt

4

现在是4点。你不能让一个通用的动物(就像你代码中的a变量)去叫。因为你同样可以说:

Animal a = new Cat();

而且代码行无法知道您是否已经完成了任务。


3

在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

蛇类没有任何吠叫方法,因此调用其父类的方法。它在控制台中打印第一行。 狗类有吠叫方法,因此父类调用它。它在控制台中打印第二行。


2

请注意,这不是一个好的设计。

当你有以下代码时:

if (x instanceof SomeClass)
{
   x.SomeMethod();
}

你正在滥用类型系统。这不是使用类的方法,也不是编写可维护的面向对象代码的方式。它很脆弱,复杂混乱,很糟糕。
你可以在基类中创建模板方法,但它们必须调用存在于基类中并在子类中被覆盖的方法。

1
“我说2,因为我们正在检查对象是否为狗;因为狗是具有吠叫方法的类,如果是,我们将调用它,并打印出:s。”
您的理解是正确的,但这不是它的工作方式。
Java 是一种静态类型语言,这意味着对象可能响应的方法的有效性在编译时进行验证。
您可能认为检查:
if( a instanceof Dog ) 

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

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

这很有帮助,因为“有时候”我们在编码时会出现拼写错误(例如,打 barck() 而不是 bark())

如果编译器没有警告我们,您将不得不在“运行时”找到它,并且不总是有清晰的消息(例如,IE 中的 javascript 会显示“意外对象”之类的内容)

尽管如此,像 Java 这样的静态类型语言仍然允许我们强制调用。在这种情况下,使用“转换”运算符 ()

像这样

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导致编译时错误

希望这可以帮到你。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接