【问题标题】: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();
}

会发生什么?

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

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

我的理解正确吗?

【问题讨论】:

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

标签: java inheritance subclass superclass


【解决方案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 方法,所以超类调用它。它将第二行写入控制台。

              【讨论】:

                猜你喜欢
                • 2023-03-14
                • 2020-05-03
                • 2016-03-24
                • 1970-01-01
                • 1970-01-01
                • 2016-01-22
                • 2017-05-13
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多