【问题标题】:Static and Dynamic Binding in JavaJava中的静态和动态绑定
【发布时间】:2015-06-23 02:14:44
【问题描述】:

请有人向我解释一下如何打印输出的最后 6 行。我知道由于静态绑定,前三行打印正确。

我不知道为什么第 5 行给出了输出,因为它是 Ipod 类型并且它没有任何歌曲方法,但它仍然打印输出。 代码如下:

package myferrari;

import mycar.*;

class IPOD
{
    void PlayMusic(Music m)
    {
        System.out.println("Music from Ipod");
    }
}

class IpodPlayer extends IPOD
{
    void PlayMusic(Music m)
    {
        System.out.println("Music from IpodPlayer");
    }

    void PlayMusic(Song m)
    {
        System.out.println("Song from IpodPlayer");
    }
}

class Music{}
class Song extends Music{}

public class Main  {

    public static void main(String [] args)
    {
        IPOD ipod = new IPOD();
        IpodPlayer iplayer = new IpodPlayer();
        IPOD ipl = new IpodPlayer();

        Music m = new Music();
        Song s = new Song();
        Music sm = new Song();

        iplayer.PlayMusic(m);
        iplayer.PlayMusic(s);
        iplayer.PlayMusic(sm);  // static binding refers to the reference type

        ipod.PlayMusic(m);      
        ipod.PlayMusic(s);
        ipod.PlayMusic(sm);

        ipl.PlayMusic(m);
        ipl.PlayMusic(s);
        ipl.PlayMusic(sm);

    }
}

输出在这里:

Music from IpodPlayer
Song from IpodPlayer
Music from IpodPlayer
Music from Ipod
Music from Ipod
Music from Ipod
Music from IpodPlayer
Music from IpodPlayer
Music from IpodPlayer

【问题讨论】:

  • 也许输出会很好:)
  • 我已经添加了 o/p :)
  • 您会说因为Song extends Music,我们应该能够将Song 传递给接受Music 的方法吗?
  • 我认为如果我们接受这一点,我们可以将第 5 行作为输出。我仍然有疑问为什么最后一行和倒数第二行不是来自 IpodPlayer 的歌曲
  • 因为 java 在编译时根据对象的类型决定使用哪种类型。注意sm 的类型是MusicplayMusic(Music) 将被选为要调用的方法。

标签: java oop binding polymorphism dynamic-binding


【解决方案1】:

坚果壳:整个概念是关于方法重载和覆盖以及它们如何与动态和静态绑定相关联。

现在,首先请看下面的 sn-p,它是您的案例@run-time 的简化,并了解运行时图片。使用您选择的任何 IDE 调试您的代码,您将看到完全相同的结果。

主要收获:在运行时,您的引用将解析为实际对象,这称为动态绑定或多态。 运行时意味着对象。所以,无论是IpodPlayer iplayer = new IpodPlayer(); 还是IPOD ipl = new IpodPlayer();,在运行时你都会得到IpodPlayer 的实例,并且它的方法会被调用。

方法覆盖链接的动态绑定。因此,在运行时将调用哪个重写方法是通过检查对象类型来决定的。因此,您可以看到,无论引用类型是什么,在运行时都会调用实际对象的 PlayMusic 方法。

这总结了您的动态绑定和方法覆盖。

    new IpodPlayer().PlayMusic(new Music());        //Music from IpodPlayer 
    new IpodPlayer().PlayMusic(new Song());         //Song from IpodPlayer 
    new IpodPlayer().PlayMusic(new Song());         //Music from IpodPlayer 

    new IPOD().PlayMusic(new Music());              //Music from Ipod     
    new IPOD().PlayMusic(new Song());               //Music from Ipod 
    new IPOD().PlayMusic(new Song());               //Music from Ipod 

    new IpodPlayer().PlayMusic(new Music());        //Music from IpodPlayer 
    new IpodPlayer().PlayMusic(new Song());         //Music from IpodPlayer 
    new IpodPlayer().PlayMusic(new Song());         //Music from IpodPlayer

来到静态绑定和方法重载。

静态绑定清楚地说明了 Java 中的重载方法如何在编译时使用类型信息进行绑定。

现在,您的IPOD ipod = new IPOD(); 情况非常清楚,因为您只有方法而没有重载方法,所以您的结果没有。 4,5 和 6 是不言自明的。

真正的混淆是IpodPlayer iplayer = new IpodPlayer(); AND IPOD ipl = new IpodPlayer();

在编译时根据对象类型决定调用哪个重载方法,是PlayMusic(Music m)还是PlayMusic(Song m)请注意,我说的只是方法,而不是哪个类或对象。

现在,在你的情况下没有。 1,2 和 3,因为 new IpodPlayer() 的对象和你有两个重载的方法所以适当的 PlayMusic 方法被调用,基于PlayMusic对象类型

最后一个案例 - 您的案例编号。 7、8 和 9 是主要的挠头案例,最重要的是要理解,因为它结合了动态绑定和静态绑定的概念。
再次在编译时根据对象类型决定要调用的重载方法,但由于在编译时它是根据IPOD ipl 而不是= new IpodPlayer(); 决定的,所以编译器决定并生成@987654336 的字节码@ 将在运行时被调用(无论是对象的方法,但 PlayMusic(Music m) 方法),因为 IPOD 没有任何其他方法。 最后在运行时动态绑定将进入图片和方法IpodPlayer 会因为= new IpodPlayer(); 而被调用

【讨论】:

    【解决方案2】:

    您的打印类有一个方法,该方法将对象作为 Music 类,而不是 Song 的子类。

    为了做你想做的事,你必须这样做:

    void PlayMusic(Music m){
      if(m instance of Song){
        System.out.println("Song from Ipod");
      } else
        System.out.println("Music from Ipod");
    }
    

    并且由于您将 ipl 声明为超类 IPOD 并将其实例化为 IPodPlayer,因此除非您检查它的类型并适当地转换它,否则它的行为就像 IPOD。

    IpodPlayer player2 = null;
    if(ipl instance of IpodPlayer)
        player2 = (IpodPlayer)ipl;
    

    但这只是没有意义,所以只需将它用作您希望它表现的类。

    这篇文章比我解释得更好。 Static Vs. Dynamic Binding in Java

    【讨论】:

      【解决方案3】:

      其中的原因: 起初,java 使用静态绑定从一组重载方法中进行选择。然后从一组覆盖的方法中选择动态绑定。 http://beginnersbook.com/2013/04/java-static-dynamic-binding/

      class IpodPlayer extends IPOD
      {
          //that is overriding
          void PlayMusic(Music m)
          {
              System.out.println("Music from IpodPlayer");
          }
      
          //that is overloading    
          void PlayMusic(Song m)
          {
              System.out.println("Song from IpodPlayer");
          }
      }
      

      【讨论】:

        【解决方案4】:

        我认为您理解为什么要打印前三行,因此我不会解释其背后的原因。

        所以这样想。每当您“扩展”到一个类时,您实际上是在创建该类的新副本并向其添加内容。 如果你把一个类想象成一个包含房间(方法)和电器(变量)的房子的蓝图,那么扩展另一个类的类就是一个房子的蓝图,它基于另一个蓝图并带有一些可能的修改/添加。当您创建扩展另一个蓝图的蓝图时,您添加的任何与原始蓝图具有相同签名(相同名称/类型/参数)的房间或设备都会覆盖原始蓝图中的那个东西。

        解释为什么每行都像现在这样打印出来:

        第 4 行:您创建了一个 IPOD 类,它只有一个方法并打印相同的内容,“来自 Ipod 的音乐”。

        第 5 行:您创建了一个 IPOD 类,它只有一个方法并打印相同的内容,“来自 Ipod 的音乐”。是的,该方法只接受 Music 类型,但 Song 扩展了 The Music。回到这个例子:这意味着音乐屋的房间和电器都在歌曲屋内。因此,由于该方法不是在寻找歌曲库,因此它会删除不属于音乐库的所有内容并将其丢弃。然后将音乐屋交给IPOD类内部的方法。

        第 6 行:您创建了一个 IPOD 类,它只有一个方法并打印相同的内容,“来自 Ipod 的音乐”。 SM 是一个音乐对象,因此很容易传递给方法。

        第 8 行:所以第 8 行是在你的 main 中使用这些代码行:

        IPOD ipl = new IpodPlayer();
        Song s = new Song();
        ipl.PlayMusic(s);
        

        ipl 属于 Ipod 类而不是 IpodPlayer 类。 IPOD 类中没有对象歌曲的方法。当你写“IPOD ipl = new IpodPlayer();”您没有创建 IpodPlayer 对象。您正在创建一个IPOD 对象。 当您在此上下文中说“新 IpodPlayer”时,您实质上是在说使用 IpodPlayer 蓝图来构建一个IPOD 对象。 IPOD 对象是具有一种方法“void PlayMusic(Music m)”的类。所以 ipl 只有一种方法(音乐方法)。 之所以说“来自 IpodPlayer 的音乐”,而不是“来自 Ipod 的音乐”,是因为当您使用 IpodPlayer 蓝图构建IPOD 对象。这只有效,因为 IpodPlayer 正在扩展 IPOD 类。

        第 7&9 行:IPL 是一个IPOD 类,但是您在声明期间使用来自IpodPlayer 类的相同命名方法覆盖名为“PlayMusic”的方法。这就是为什么 m、s 和 sm 都打印出“来自 IpodPlayer 的音乐”

        【讨论】:

        • 第8行,IpodPlayer类中有一个Object Song的方法,还是转到Music Object的方法,为什么会这样?
        • 我在回答中为第 8 行添加了更深入的解释。希望对您有所帮助!
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-08-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-29
        • 1970-01-01
        相关资源
        最近更新 更多