您好,我有一个基本的代码特性问题,如下所示。当我更改代码时,我会得到奇怪的输出。下面的程序给出了正确的输出,因为我们知道 Java 默认不支持 Double Dispatch。要求您查看下面的代码并查看输出。之后我修改了代码并得到了奇怪的输出。
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;
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);
}
}
}
这里的输出是 ... 开设储蓄账户 ... ... 成功开设储蓄账户 ... ... 开设储蓄账户 ... ...成功开设Demat账户...
现在我的问题是我得到了我期望的结果,为什么我应该选择访客模式,在上面的代码中即使它显示“正在保存帐户”,但它正确执行“Demat帐户代码”部分。
问题出在哪里?
简短回答:Java 没有运行时参数多态性(运行时根据参数类型选择方法)。
编译器将在对象上多态地调用子类方法,因此如果
act是 DematAccount,则
act.open()
将调用 DematAccount 的实现。然而,Java 的多态性不适用于参数,因此,如果 act是
SavingAccount类型的变量,无论其运行时类型如何,
bank.open(act)
将始终调用 open(SavingAccount)
。
编译器在调用站点知道的相关信息是 act 是一个 SavingAccount 并且 bank 有一个方法 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);
}
}
这有点丑陋,意味着将此代码耦合到各种帐户并在每次更改时更改它。
在 Bank os 中更改它会更好,因为 Bank 已经承担了了解所有 SavingAccount 子类的责任。
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仅在控制台输出中有所不同,您可以轻松地将控制台输出移动到SavingAccount和DematAccount,并从Bank中删除open(DematAccount)。