Java 中的静态和动态绑定

Static and Dynamic Binding in Java

谁能给我解释一下输出的最后 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

您执行打印的 class 有一个方法将对象作为音乐 class,而不是歌曲的子class。

为了做你想做的事,你必须做如下:

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

并且由于您将 ipl 声明为超级 class IPOD 并将其实例化为 IPodPlayer,除非您检查它的类型并适当地转换它,否则它的行为与 IPOD 相同。

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

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

这个 post 比我解释得更好。 Static Vs. Dynamic Binding in Java

原因在于: 起初 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");
    }
}

我想你明白为什么打印前三行,所以我不解释背后的原因。

这么想。每当您"extends" 到 class 时,您实际上是在创建该 class 的新副本并向其中添加内容。 如果您将 class 想象成一个拥有房间(方法)和电器(变量)的房子的蓝图,那么扩展另一个 class 的 class 就是一个基于用一些可能 modifications/additions 关闭另一个蓝图。当您创建扩展另一个蓝图的蓝图时,您添加的任何房间或设备与原始蓝图中的东西具有相同的签名(相同 name/type/arguments),将覆盖原始蓝图中的那个东西。

解释为什么每一行都这样打印出来:

第 4 行: 你创建了一个 IPOD class,它只有一个方法并打印相同的东西,"Music from Ipod".

第 5 行:您创建了一个 IPOD class,它只有一个方法并打印相同的东西,"Music from Ipod"。是的,该方法只接受 Music 类型,但 Song 扩展了 The Music。回到这个例子:也就是说音乐屋的房间和电器都在歌屋里面。因此,由于该方法不是在寻找歌曲之家,因此它会删除不属于音乐之家的所有内容并将其丢弃。然后把音乐屋交给IPOD里面的方法class.

第 6 行:您创建了一个 IPOD class,它只有一个方法并打印相同的东西,"Music from Ipod"。 SM 是一个音乐对象,因此很容易将其传递给方法。

第 8 行:所以第 8 行是在您的主程序中使用这些代码行组成的:

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

ipl 属于 class IPOD 而不是 IpodPlayer Class。 IPOD class 中没有对象歌曲的方法。当您编写 "IPOD ipl = new IpodPlayer();" 时,您并不是在创建 IpodPlayer 对象。您正在创建一个 IPOD 对象。 当您在此上下文中说 "new IpodPlayer" 时,您实际上是在说使用 IpodPlayer 蓝图构建一个 IPOD 对象。 IPOD 对象是 class 和一种方法 "void PlayMusic(Music m)"。所以 ipl 只有一种方法(音乐方法)。 它说 "Music from IpodPlayer" 而不是 "Music from Ipod" 的原因是因为当您使用 IpodPlayer 蓝图构建IPOD 对象。这只有效,因为 IpodPlayer 正在扩展 IPOD class。

第 7 行和第 9 行:IPL 是一个 IPD class 但是您使用 IpodPlayer [=52= 中的相同命名方法覆盖了名为 "PlayMusic" 的方法] 申报时。这就是为什么m、s、sm都打印出"Music from IpodPlayer"

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

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

主要收获: 在 运行 时,您的引用将解析为实际对象,这称为 动态绑定或多态性. 运行-time表示object. 所以,无论是IpodPlayer iplayer = new IpodPlayer(); OR 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();IPOD ipl = new IpodPlayer(); 的情况下。

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

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

最后一个案例 - 您的案例编号。 7、8 和 9 是主要的令人头疼的案例,理解起来最重要,因为它结合了动态绑定和静态绑定的概念。
同样在编译时,要调用的重载方法是根据对象类型决定的,但由于在编译时它是根据 IPOD ipl 而不是 = new IpodPlayer(); 来决定的,所以编译器决定并生成PlayMusic(Music m) 将在 运行 时间被调用的字节码(对象的方法但是 PlayMusic(Music m) 方法)因为 IPOD 没有任何其他方法。 最后在 运行 时动态绑定将出现,并且 IpodPlayer 的方法将被调用,因为 = new IpodPlayer();