Что это за особенность? Почему я должен использовать решение Double Dispatch, например шаблон проектирования посетителей?
Привет, у меня есть проблема с особенностями кода, приведенная ниже. Я получаю своеобразный вывод, когда меняю код. Приведенная ниже программа выдает правильный вывод, поскольку мы знаем, что 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 Account Code".
Пожалуйста, объясните мне, где проблема? Заранее спасибо.
1 ответ
Краткий ответ: Java не имеет параметрического полиморфизма во время выполнения (выбор метода на основе типа параметра во время выполнения).
Компилятор вызовет метод подкласса полиморфно для объекта, так act.open()
вызовет реализацию DematAccount, если act является DematAccount. Тем не менее, полиморфизм Java не работает с параметрами, поэтому bank.open(act)
всегда буду звонить open(SavingAccount)
if act - переменная типа SavingAccount независимо от ее типа во время выполнения.
Соответствующее, что компилятор знает на месте вызова, - это то, что act является SavingAccount, и у этого банка есть метод open(SavingAccount) в качестве ближайшего контравариантного соответствия:
for(SavingAccount act : actList ) {
bank.open(act);
}
Чтобы обойти это, вы могли бы сделать набор типов с instanceof и привести акт к подклассу, или вы могли бы сделать этот набор типов внутри класса 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 и удалить open(DematAccount) из Bank.