instanceof与多态性的对比,取款举例

通过 instanceof 判断每个账户对象是否是 SavingsAccount 或 CreditCardAccount 类型,然后进行相应的类型转换,并调用具体的 withdraw() 方法。虽然这样能够针对不同类型的账户执行不同的逻辑,但在面向对象设计原则中,更推荐使用多态性来避免这种类型检查和强制转换,在某些场景下,instanceof 仍然有用,例如当需要执行不同类型对象具有显著差异的行为时。

使用多态

在Java中,声明一个变量时,可以使用父类类型来引用子类对象。这样做主要是为了实现多态性(Polymorphism),即允许将子类的对象当作父类的对象处理,这样在设计上更加灵活和通用。

例如,在上述代码中:

Account creditCardAccount = new CreditCardAccount(500.0, 2000.0);

CreditCardAccount 类继承自 Account 类,意味着 CreditCardAccount 是一种特殊的 Account。通过将 creditCardAccount 声明为 Account 类型的引用,我们可以将其视为任何实现了 Account 接口或者继承了 Account 类的具体账户类型,这有利于编写更通用的方法或逻辑,比如处理不同类型的账户。

尽管如此,当需要调用 CreditCardAccount 特有的方法或属性时,就需要进行类型转换,因为 Account 类型的引用并不能直接访问子类 CreditCardAccount 的特有成员。所以,在后续代码中出现了 ( (CreditCardAccount) creditCardAccount) 这样的类型转换表达式。

使用instance of

在Java中,当一个对象被声明为父类类型的引用指向子类的对象时,如果要访问子类特有的方法或属性,就需要将父类引用显式地转换为子类类型。例如,在上述代码中:

Account creditCardAccount = new CreditCardAccount(500.0, 2000.0);

这里 creditCardAccount 虽然是 Account 类型的引用,但实际上它指向的是 CreditCardAccount 类的一个实例。由于 Account 类可能是抽象类或者不包含特定于 CreditCardAccount 的方法和属性,所以当我们需要调用 CreditCardAccount 特有的方法时,就必须进行类型转换。

if (creditCardAccount instanceof CreditCardAccount) {
    ((CreditCardAccount) creditCardAccount).withdraw(1500.0);
}

这段代码首先检查 creditCardAccount 是否实际上是 CreditCardAccount 类的一个实例(通过 instanceof 关键字),如果是,则将其强制转换为 CreditCardAccount 类型,然后就可以安全地调用 CreditCardAccount 类独有的 withdraw() 方法或其他特有成员了。这样做的目的是为了确保在编译期间和运行期间能够正确访问子类的特性,而不会引发类型错误。

以下是完整示例代码:

public abstract class Account {
    protected String accountNumber;
    protected String ownerName;
    protected double balance;

    public abstract boolean deposit(double amount);

    public abstract boolean withdraw(double amount);
}
public class SavingsAccount extends Account {
    @Override
    public boolean deposit(double amount) {
        if (amount > 0) {
            balance += amount;
            System.out.println("存款成功,当前余额:" + balance);
            return true;
        } else {
            System.out.println("存款金额必须大于0");
            return false;
        }
    }

    @Override
    public boolean withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            this.balance -= amount;
            System.out.println("已成功从储蓄账户中取款:" + amount + ",当前余额:" + balance);
            return true;
        } else {
            System.out.println("取款金额超出账户余额");
            return false;
        }
    }
}
public class CreditCardAccount extends Account {
    private double creditLimit;

    public CreditCardAccount(double initialBalance, double creditLimit) {
        this.balance = initialBalance;
        this.creditLimit = creditLimit;
    }

    public double getCreditLimit() {
        return this.creditLimit;
    }

    public void setCreditLimit(double limit) {
        this.creditLimit = limit;
    }

    @Override
    public boolean deposit(double amount) {
        // 假设信用卡不能存款
        System.out.println("信用卡账户暂不支持存款功能");
        return false;
    }

    @Override
    public boolean withdraw(double amount) {
        if (amount > 0 && amount <= (this.balance + this.creditLimit)) {
            this.balance -= amount;
            System.out.println("已成功从信用卡账户中消费:" + amount + "当前余额:" + balance);
            return true;
        } else {
            System.out.println("消费金额超出信用额度或账户余额");
            return false;
        }
    }
}
import java.util.List;

public class BankSystem {
    public void withdrawFromAllAccounts(List<Account> accounts, double amount) {
        for (Account account : accounts) {
            //多态性
            if (account.withdraw(amount)) {
                System.out.println("从" + account.getClass().getSimpleName() + "成功取款/消费:" + amount);
            }

//            //instance of
//            boolean withdrawlSuccess = false;
//            if (account instanceof SavingsAccount) {
//                SavingsAccount savingsAccount = (SavingsAccount) account;
//                withdrawlSuccess = savingsAccount.withdraw(amount);
//                if (withdrawlSuccess) {
//                    System.out.println("从储蓄账户成功取款" + amount);
//                }
//            } else if (account instanceof CreditCardAccount) {
//                CreditCardAccount creditCardAccount = (CreditCardAccount) account;
//                withdrawlSuccess = creditCardAccount.withdraw(amount);
//                if (withdrawlSuccess) {
//                    System.out.println("从信用卡账户成功消费" + amount);
//                }
//            }

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

public class Main {
    public static void main(String[] args) {
        Account savingAccount = new SavingsAccount();
        savingAccount.deposit(1000.0);

        // 使用instanceof检查并执行特定类型的账户操作
        Account creditCardAccount = new CreditCardAccount(500.0, 2000.0);
//        if (creditCardAccount instanceof CreditCardAccount) {
//            ((CreditCardAccount) creditCardAccount).withdraw(1500.0);
//        }
//        // 根据账户类型决定是否展示信用额度信息
//        if (creditCardAccount instanceof CreditCardAccount) {
//            CreditCardAccount ccAccount = (CreditCardAccount) creditCardAccount;
//            System.out.println("该信用卡账户的信用额度为" + ccAccount.getCreditLimit());
//        }

        List<Account> accounts = new ArrayList<>();
        accounts.add(savingAccount);
        accounts.add(creditCardAccount);
        BankSystem bankSystem = new BankSystem();
        double withdrawAmount = 300.0;
        bankSystem.withdrawFromAllAccounts(accounts, withdrawAmount);
    }
}

使用this

在Java中,this.balance -= amount; 这行代码是在当前类的实例方法内部访问并修改实例变量 balance 的值。这里的 this 关键字是指代当前对象的一个引用,用于明确地指示我们操作的是当前对象的成员变量。

虽然在某些情况下,如果局部作用域内没有其他同名变量与成员变量冲突,可以省略 this 关键字,直接写成 balance -= amount;。但是为了增强代码的可读性和避免潜在的命名冲突问题,推荐始终在访问成员变量时使用 this. 前缀。

特别是在复杂的类结构或匿名内部类、lambda表达式等场景下,使用 this. 明确指向成员变量是非常必要的。因此,在这个示例中,即使可以省略 this., 但为了更好的编码习惯和可维护性,建议保留 this.balance -= amount; 的形式。

Account 类被设计为抽象类而不是接口

  1. 包含具体方法实现
    抽象类可以包含抽象方法(即没有具体实现的方法),也可以包含具体方法。在示例中虽然未给出具体的实现方法,但在实际场景中,如果有一些通用的方法逻辑可以直接在抽象类中实现并供子类复用,那么使用抽象类就比接口更合适。例如,可能存在一个计算账户利息的通用算法,该算法可以定义在抽象类中。

  2. 状态和行为封装
    抽象类允许定义成员变量(如 balance 在示例中),这样就可以存储和操作状态信息。而接口只能定义方法签名,不能有实例变量。金融系统中的账户通常需要维护一些状态信息,因此抽象类能更好地封装这些状态和相关的行为。

  3. 模板方法设计模式
    虽然在示例中没有展示,但有时候抽象类可以作为“模板”,通过定义一个或多个模板方法来控制子类部分方法的执行流程,这是接口无法做到的。

  4. 继承层次复杂度
    如果预计将来会有较多的不同类型的账户,并且它们之间有一定的层级关系或者共享较多代码时,采用抽象类有助于构建更加合理的继承层次结构。

在这个特定的例子中,选择抽象类是因为它允许提供更多的灵活性和扩展性,能够同时定义抽象方法以及实现某些共有的、不依赖于具体子类的方法。如果只是单纯地为了规范行为,而不需要任何状态或共同的实现,使用接口会更为恰当。

balance

在金融和银行术语中,balance通常指的是账户余额,即某一特定时刻账户内的资金总额。这个数值会随着存款(入金)、取款(出金)、利息计算、交易费用或其他任何导致账户金额变动的操作而发生变化。

在外汇交易(Forex)领域,balance同样指交易账户中的净值或余额,它反映了投资者在账户中的总资产减去总负债后的剩余金额。当交易者进行买卖货币对操作时,盈利或亏损都会反映在balance上。

SavingsAccount

表示专用于存储个人或企业定期或不定期存款,并通常能获取一定利息收益的银行账户类型。

你可能感兴趣的:(java,开发语言)