简体   繁体   中英

Subclass access of Private member variables in java

I've been really stumped on this question from "Data Structures and Algorithms" by Goodrich and Tamassia. The book presents the following two classes:

public class CreditCard {
private String customer;
private String bank;
private String account;
private int limit;
protected double balance;

public CreditCard(String customer, String bank, String account, int limit, double balance) {
    this.customer = customer;
    this.bank = bank;
    this.account = account;
    this.limit = limit;
    this.balance = balance;

}

public CreditCard(String customer, String bank, String account, int limit) {
    this(customer, bank, account, limit, 0.0);
}

public String getCustomer() { return this.customer; }
public String getBank() { return this.bank; }
public String getAccount() { return this.account; }
public int getLimit() { return this.limit; }
public double getBalance() { return this.balance; }

public boolean charge(double price) {
    if(price + this.balance > this.limit)
        return false;

    this.balance += price;
    return true;
}

public void makePayment(double amount) {
   if( amount < 0)
      System.out.println("Cannot process negative payment");
   else
      this.balance -= amount;
}

public void updateCreditLimit() {
    this.limit -= balance;
}

public static void printSummary(CreditCard card) {
    System.out.println("Customer = " + card.customer);
    System.out.println("Bank = " + card.bank);
    System.out.println("Account = " + card.account);
    System.out.println("Balance = " + card.balance);
    System.out.println("Limit = " + card.limit);
}

}

Subclass

public class PredatoryCreditCard extends CreditCard {
private double apr;

public PredatoryCreditCard(String customer, String bank, String account,
                           int limit, double balance, double apr) {
                               super(customer, bank, account, limit, balance);
                               this.apr = apr;
                           }

public void processMonth() {
    if(this.balance > 0) {
        double monthlyFactor = Math.pow(1 + apr, 1F/12);
        this.balance *= monthlyFactor;
    }
}

public boolean charge(double price) {
    boolean isSuccess = super.charge(price);
    if(!isSuccess)
        this.balance += 5;
    return isSuccess;
}

}

Question:

Assume that we change the CreditCard class so that instance variable balance has private visibility. Why is the following two implementations of the PredatoryCreditCard.charge method flawed ?

public boolean charge(double price) {
    boolean isSuccess = super.charge(price);
    if(!isSuccess)
       charge(5); //penalty for late fee
     return isSuccess;
 }

Second one:

public boolean charge(double price) {
    boolean isSuccess = super.charge(price);
    if(!isSuccess)
       super.charge(5); //penalty for late fee
     return isSuccess;
 }

What I understand is that a subclass can not directly manipulate a private field of it's super class. The field must be protected or public. This allows us to say in the subclass this.balance += 5 . My understanding of the principles is not the issue, my problem is with the question i'm asked to answer. From an implementation stand point, there is no flaw that I can clearly see in the PredatoryCreditCard.charge method as in both cases of the new implementation provided by the question, we are changing the balance field of the class regardless, because of the call to super. I can't find a flaw in the new implementation unless I think there is some hole in my knowledge of inheritance.

Thank you in advance.

In either case, you can't be charged a fee if you are close enough to the balance that the fee (of value 5) would exceed your limit. And in the first case:

public boolean charge(double price) {
    boolean isSuccess = super.charge(price);
    if(!isSuccess)
       charge(5); //penalty for late fee
     return isSuccess;
 }

What happens here when isSuccess fails? We get stuck in infinite recursion calling this.charge(...)

There are no syntax errors in either implementation. Both have (or may have) logic errors.

First Implementation

public boolean charge(double price) {
    boolean isSuccess = super.charge(price);
    if(!isSuccess)
       charge(5);  //penalty for late fee
     return isSuccess;
 }

We start one level deep.

The first statement is super.charge(price) .
If that statement returns false , then we call this.charge(5) .
We are now two levels deep.

The first statement is super.charge(5) .
If that statement returns false , then we call this.charge(5) .
We are now three levels deep.

The first statement is super.charge(5) .
If that statement returns false , then we call this.charge(5) .
We are now four levels deep.
...

You get the point. The first implementation can cause infinite recursion, which will then cause a stack overflow. See What methods are there to avoid a stack overflow in a recursive algorithm?

Second Implementation

public boolean charge(double price) {
    boolean isSuccess = super.charge(price);
    if(!isSuccess)
       super.charge(5); //penalty for late fee
     return isSuccess;
 }

We try to charge price . We record whether the transaction succeeded.
If the transaction fails, we try to charge a late fee. We do not record whether the transaction succeeded.
If the late fee fails, we do not care.

I am not sure what the error is here. It certainly does not mimic how my bank works, but that is not necessarily a problem. I assume that avoiding late fees is a bad thing, but that really depends on your requirements.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM