Java 这是什么特点?为什么我应该选择访客设计模式这样的双重分派解决方案?

Java 这是什么特点?为什么我应该选择访客设计模式这样的双重分派解决方案?,java,oop,core,dispatch,Java,Oop,Core,Dispatch,嗨,我有一个基本的代码特性问题,如下所示。当我更改代码时,会得到特殊的输出。下面的程序给出了正确的输出,因为我们知道Java默认情况下不支持双重分派。请您查看下面的代码并查看输出。之后,我修改了代码,得到了奇怪的输出 import java.util.ArrayList; import java.util.List; class SavingAccount { } class DematAccount extends SavingAccount { } class Bank {

嗨,我有一个基本的代码特性问题,如下所示。当我更改代码时,会得到特殊的输出。下面的程序给出了正确的输出,因为我们知道Java默认情况下不支持双重分派。请您查看下面的代码并查看输出。之后,我修改了代码,得到了奇怪的输出

import java.util.ArrayList;
import java.util.List;

class SavingAccount {

}

class DematAccount extends SavingAccount {

}

class Bank {

    public void open(SavingAccount act ) {
        System.out.println("... Opening Saving Account ...");
    }

    public void open(DematAccount act ) {
        System.out.println("... Opening Demat Account ...");
    }
}

public class Test {
    public static void main(String[] args) {
        List<SavingAccount> actList = new ArrayList<SavingAccount>();

        Bank bank = new Bank();

        actList.add( new SavingAccount());
        actList.add( new DematAccount());

        for( SavingAccount act : actList ) {
            bank.open(act);
        }
    }
}
import java.util.ArrayList;
导入java.util.List;
阶级储蓄帐户{
}
类DematAccount扩展了SavingAccount{
}
班级银行{
公开作废(储蓄账户法){
System.out.println(“…开立储蓄账户…”);
}
公开无效(DematAccount法案){
System.out.println(“…开立Demat账户…”);
}
}
公开课考试{
公共静态void main(字符串[]args){
List actList=新建ArrayList();
银行=新银行();
添加(新的SavingAccount());
add(new DematAccount());
对于(储蓄账户法案:actList){
银行公开(act);
}
}
}
下面给出了输出。 。。。开立储蓄帐户。。。 ... 开立储蓄账户…

现在让我修改代码并查看下面的输出

import java.util.ArrayList;
import java.util.List;

class SavingAccount {

    public void open() {
        System.out.println("... Opened Saving Account Successfully ...");
    }

}

class DematAccount extends SavingAccount {

    public void open() {
        System.out.println("... Opened Demat Account Successfully ...");
    }
}

class Bank {

    public void open(SavingAccount act ) {
        System.out.println("... Opening Saving Account ...");
        act.open();
    }

    public void open(DematAccount act ) {
        System.out.println("... Opening Demat Account ...");
        act.open();
    }
}

public class Test {
    public static void main(String[] args) {
        List<SavingAccount> actList = new ArrayList<SavingAccount>();

        Bank bank = new Bank();

        actList.add( new SavingAccount());
        actList.add( new DematAccount());

        for( SavingAccount act : actList ) {
            bank.open(act);
        }
    }
}
import java.util.ArrayList;
导入java.util.List;
阶级储蓄帐户{
公开作废{
System.out.println(“…已成功开户…”);
}
}
类DematAccount扩展了SavingAccount{
公开作废{
System.out.println(“…已成功打开Demat帐户…”);
}
}
班级银行{
公开作废(储蓄账户法){
System.out.println(“…开立储蓄账户…”);
打开()的动作;
}
公开无效(DematAccount法案){
System.out.println(“…开立Demat账户…”);
打开()的动作;
}
}
公开课考试{
公共静态void main(字符串[]args){
List actList=新建ArrayList();
银行=新银行();
添加(新的SavingAccount());
add(new DematAccount());
对于(储蓄账户法案:actList){
银行公开(act);
}
}
}
这里是输出 ... 开立储蓄帐户。。。 ... 已成功打开储蓄帐户。。。 ... 开立储蓄帐户。。。 ... 已成功打开Demat帐户

现在我的问题是,我得到了我期望的结果,为什么我要在上面的代码中选择访问者模式,即使它显示“Saving Account”,但它正确地执行了“Demat Account code”部分

请给我解释一下,问题出在哪里?
提前感谢。

简短回答:Java没有运行时参数多态性(根据运行时的参数类型选择方法)

编译器将多态地调用对象上的子类方法,因此
act.open()
将调用DematAccount的实现,如果actDematAccount。但是,Java的多态性对参数不起作用,因此
bank.open(act)
将始终调用
open(SavingAccount)
如果actSavingAccount类型的变量,而不管其运行时类型如何

编译器在调用站点知道的相关信息是,act是一个SavingAccountbank有一个方法open(SavingAccount)作为最接近的逆变匹配:

for(SavingAccount act : actList ) {
    bank.open(act);
}
您可以使用instanceof执行类型大小写,并将act强制转换到子类以解决此问题,也可以在Bank类中执行该类型大小写

for(SavingAccount act : actList ) {
    if (act instanceof DematAccount) {
        bank.open((DematAccount) act);
    } else {
        bank.open(act);
    }
}
这有点难看,意味着将此代码耦合到各种帐户,并在每次更改时进行更改

银行操作系统内部更好地更改它,因为银行已经承担了了解所有储蓄账户子类的责任

class Bank {

    public void open(SavingAccount act ) {
        if (act instanceof DematAccount) {
            open((DematAccount) act);
        } else {
            System.out.println("... Opening Saving Account ...");
            act.open();
        }
    }

    public void open(DematAccount act ) {
        System.out.println("... Opening Demat Account ...");
        act.open();
    }
}

在这种情况下,Bank仅在控制台输出上不同,您可以轻松地将该控制台输出移动到SavingAccountDematAccount,并从Bank中删除open(DematAccount),问题在于您没有使用双重分派/访问者。从内部
DematAccount
它需要调用一个类型为
DematAccount
的方法。当你呼叫银行时,打开(act)在循环中,您使用的是
SavingAccount
版本,因为这是您在循环中使用的类型。我没有使用访问者模式,请查看代码的第一部分,其中它提供了不同的结果,但在第二部分中,我得到了其他内容。如果我有问题,我将应用访问者模式,但在这里,一切似乎都很好。我的问题是为什么我们会得到一些不同的结果。这并不是真的不同。它会打印相同的“期初储蓄账户”消息,但随后您会调用一个实现方式不同的方法,并成功打印Demat或Save opened。方法调用是在编译时确定的,编译时是运行时类的实现。嗨,阿兰,如果是这种情况,那么它为什么要在“act.open()”方法中调用运行时类型?@Sambit ah。这是因为Java的调用在对象上是多态的
act.open()
可以调用子类方法,因为运行时有要调用它的对象,但是
bank.open(act)
不能调用带有子类参数的方法,因为bank是决定不执行多态性的对象,bank有一个open()方法用于SavingAccount,所以必须调用它。