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

时间:2009-05-22 17:06:02

标签: java inheritance subclass superclass

动物是狗的超类 和狗有一种叫做树皮的方法

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

请考虑以下事项:

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

会发生什么?

  1. 不允许转让
  2. 允许对树皮的调用,并在运行时打印“woof”
  3. 允许对树皮的召唤,但不打印任何内容
  4. 对bark的调用导致编译时错误
  5. 对bark的调用导致运行时错误
  6. 我说2,因为我们正在检查对象是否是狗;因为狗是带有树皮方法的类,如果是,那么我们称它为打印出来的:s

    我的理解是否正确?

9 个答案:

答案 0 :(得分:32)

由于Animal没有名为bark的方法,因此无法编译。想一想,所有的狗都是动物,但不是所有的动物都是狗。所有的狗都会吠叫,但不是所有的动物都会吠叫。

答案 1 :(得分:27)

不 - 答案是;

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

bark方法未定义为已分配类型Animal的方法,因此会导致编译时间问题;这可以通过铸造来解决;

((Dog)a).bark();

答案 2 :(得分:11)

密钥位于以下行:

Animal a = new Dog();

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

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

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是一种静态类型的语言,因此不允许使用这种类型的代码。

答案 3 :(得分:5)

Head First Java中,他们使用电视遥控器的非常好的类比来获取参考,将电视用作参考指向的对象。如果你的遥控器只有按钮(方法)可以打开,关闭,频道上下,以及音量增大和减小,那么电视的酷炫功能并不重要。您仍然只能从遥控器上执行这些基本操作。例如,如果遥控器没有静音按钮,则无法将电视静音。

动物参考只知道动物方法。底层对象的其他方法无关紧要,您无法从Animal引用中访问它们。

答案 4 :(得分:4)

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

Animal a = new Cat();

并且树皮线没有办法知道你没有。

答案 5 :(得分: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();
}  

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

答案 6 :(得分:2)

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

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

if (x instanceof SomeClass)
{
   x.SomeMethod();
}
你正在滥用类型系统。这不是使用类的方法,它不是编写可维护的面向对象代码的方法。它很脆弱。这是令人费解的。这很糟糕。

您可以在基类中创建模板方法,但是它们必须调用基类中存在的方法并在子类中重写。

答案 7 :(得分:2)

在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没有任何树皮方法,所以超级班级'方法被调用。它将第一行写入控制台。 狗有一种树皮方法,所以超类叫它。它将第二行写入控制台。

答案 8 :(得分:1)

  

“我说2,因为我们正在检查对象是否是狗;因为狗是带有树皮方法的类,如果是,那么我们称之为打印出来的:s”

你的理由是正确的,但这不是它的工作方式。

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

您可以考虑检查:

if( a instanceof Dog ) 

会这样做,但事实上并非如此。编译器所做的是检查声明类型的“接口”(在本例中为Animal)。 “接口”由Animal类声明的方法组成。

如果 bark()方法未在超类动物中定义,编译器会说:“嘿,这不起作用”。

这很有用,因为“有时候”我们在编码时会输入拼写错误(例如输入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是否是有效消息。

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

演员也可以缩写为:

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

那将会运行。

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

我希望这会有所帮助。