Skip to main content
Logo image

Problem Solving with Algorithms and Data Structures using Java: The Interactive Edition

Section A.2 Composition and Inheritance

Two important concepts in object-oriented programming are composition and inheritance. Let’s examine them in detail.

Subsection A.2.1 Composition

Composition happens when an object is made up of other objects. For example, in a card game, we might represent a Deck as made up of an array of Card objects:
public class Deck {
    private Card[] cards;
    ...
}
Listing A.2.1.
Composition is also known as a Has-A relationship. (A Deck has an array of Cards). Put formally, the Deck is the aggregating class, and the Card class is is the aggregated class.
When we draw a UML diagram involving composition, as in Figure A.2.2, we use an arrow with a diamond head to indicate aggregation, and we can also add notation here that shows that one Deck contains many Card objects.
Figure A.2.2. UML Diagram Showing Aggregation - a Deck has Card(s)
Let’s use composition to build a Java simulation of a toaster. What things is a toaster built from?
  • A chassis with a number of slots (at least one, at most four)
  • A lever to push bread down or pop it out
  • A power supply to turn the toaster on and off
  • A dial to control the darkness (1=light, 10=dark)
The first two of these are built by the toaster company. But the company that makes toasters doesn’t build the power supply or the dial (which has circuitry to control the current flow). Instead, those are parts they order from some other companies and put into the chassis.
That means that our Toaster has-a PowerSupply object and has-a Dial object. Figure A.2.3 shows the UML diagram. You can see the full code in the file ch14/ToasterTest.java in the repository.
Figure A.2.3. UML Diagram Showing a Toaster composed of a PowerSupply and Dial)
Before we see how composition affects the way we construct and manipulate objects, let’s give a few more instances of composition (has-a) relationships. Using composition reflects the way objects are built out of other objects (parts). Each of the sub-parts has its own attributes and things that it can do (its methods). Notice that we sometimes need multiple instances of a sub-part when constructing the larger object.
  • A printer has a power supply, printer drum, and toner cartridge.
  • A bicycle has a gear assembly, handbrakes (2), and tires (2).
  • A refrigerator has a power supply, an icemaker, and a compressor.
  • A window in a word processor has a text area, a ribbon (icons for manipulating text), and two scroll bars (horizontal and vertical).
Back to the toaster. Listing A.2.4 shows the constructors for the PowerSupply and Dial classes:
public PowerSupply(int voltage) {
    if (voltage == 220) {
        this.voltage = voltage;
    } else {
        this.voltage = 110;
    }

    // new power supplies are always turned off
    this.turnedOn = false;
}

public Dial(int minValue, int maxValue) {
    this.minValue = minValue;
    this.maxValue = maxValue;
    // new dials are always set to lowest value
    this.dialValue = minValue;
}
Listing A.2.4. Constructors for PowerSupply and Dial
Now, look at how the Toaster class starts, with line numbers for reference:
class Toaster {
    private int nSlots;
    private int nSlices;
    private PowerSupply power;
    private Dial darkness;

    public Toaster(int nSlots, int voltage) {
        this.nSlots = Math.max(1, Math.min(4, nSlots));
        this.nSlices = 0;
        this.power = new PowerSupply(voltage);
        this.darkness = new Dial(1, 10);
    }
    // ...
}
Listing A.2.5.
The constructor in line 7 has two parameters: the number of slots in the toaster and what voltage it should have. The number of slots and number of slices of bread currently in the toaster are attributes belonging to the Toaster class, and those get set directly.
The power supply is an object, which is why line 10 has to call the PowerSupply constructor to build a power supply with the desired voltage. Think of this as the toaster company calling up the power supply company and telling them “send me a 110-volt power supply” and putting that power supply into the finished toaster.
Similarly, line 11 has to call the Dial constructor to build a dial with a range of 1-10.
Although a real-life toaster is composed of parts, all of the controls are on the toaster’s exterior. We don’t expect—or want—the customer to have to directly access the power supply to turn the toaster on! Similarly, we want to provide methods in the Toaster class that will give programs that use the class access to the private methods and attributes of power and darkness:
public boolean isTurnedOn() {
    return this.power.isTurnedOn();
}

public void setTurnedOn(boolean turnedOn) {
    this.power.setTurnedOn(turnedOn);
}

public double getDialValue() {
    return this.darkness.getDialValue();
}

public void setDialValue(double dialValue) {
    this.darkness.setDialValue(dialValue);
}
Listing A.2.6. Methods for Access to Composed Classes
Now we can write a program that creates a Toaster object and makes it do things:
public class ToasterTest {
    public static void main(String[] args) {
        Toaster euroFour = new Toaster(4, 220);

        euroFour.setTurnedOn(true);
        euroFour.setDialValue(4.5);
        euroFour.insertBread(1);

        System.out.println(euroFour);
    }
}
Listing A.2.7.
To summarize this discussion of composition:
  • Use composition when you have objects that are built up from other objects.
  • Provide methods in an object to give users access to private attributes and methods of the sub-objects.

Subsection A.2.2 Inheritance

An Is\-A relationship is known as inheritance:
  • An electric bicycle is a bicycle (with extra attributes and capabilities)
  • A trumpet is a bugle with valves (keys) that give the ability to play more notes
  • An alarm clock is a desk clock with extra attributes and capabilities
  • In a computer application, a “combo box” is a drop-down menu with extra capabilities (you can type the value as well as select it from the list)
Without inheritance, we would have to represent the Bicycle and ElectricBicycle classes as shown in Figure A.2.8:
Figure A.2.8. Separate Classes for Bicycle and ElectricBicycle
That’s a lot of duplicated code when we translate it to Java. If we can use the extends keyword to say that ElectricBicycle extends Bicycle, that means that ElectricBicycle inherits all of Bicycle’s methods and attributes. All we have to include now in ElectricBicycle are the attributes and methods that add extra capabilities to an electric bicycle, as shown in Figure A.2.9:
Figure A.2.9. ElectricBicycle Extending the Bicycle Class
In this example, Bicycle is called the superclass or parent class, and ElectricBicycle is called the subclass or child class. We use an open-headed arrow pointing from the subclass to the superclass in the UML diagram. As in the real world, the child inherits things from the parent
 1 
With one exception: as the joke says, “Parents inherit their gray hair from their children.”
.
Before we go into details about how to use inheritance in Java, let’s take a break for a short exercise:

Checkpoint A.2.10.

In this exercise, we will explore has-a and is-a relationships. First, state whether the relationship of the following classes is composition or inheritance, and draw the UML diagram showing that relationship. (You don’t need to provide attributes or methods in the diagrams; the class name alone will be sufficient.)
  • Address and Student
  • Car and Vehicle
  • Account and SavingsAccount
  • State, Capital, and Country
  • Instructor, Course, and Textbook
  • Dog, Cat, and Animal
  • Rectangle, Circle, Square, and Shape
For classes that exhibit the inheritance relationship, could you name a few data fields/attributes for the superclass? Could you name a few for the subclass only?
For example, Teacher (subclass) is-a Person (superclass). Data fields for Person are: name, age, address, etc. Data fields for Teacher are: school, hire date, etc.
Let’s use a smaller example for our continued discussion of inheritance. We’ll have an Item class, which represents an item in a store. An Item instance has a name, a SKU (stock keeping unit), and a price. This is the parent class. We also have a SaleItem class, which is a child class. In addition to a name, SKU, and price (which it inherits from the parent class), a SaleItem has a discount percentage (expressed as a decimal). Figure A.2.11 shows the UML diagram for these two classes:
Figure A.2.11. UML Diagram of Item and SaleItem classes
There are no setters for the name and sku attributes; once an item is created, those attributes should never change.
Listing A.2.12 shows the code for Item, which you will find in the repository.
public class Item {
    private String name;
    private String sku;
    private double price;

    public Item(String name, String sku, double price)  {
        this.name = name;
        this.sku = sku;
        this.price = Math.abs(price);
    }

    public String getName() {
        return this.name;
    }

    public String getSku() {
        return this.sku;
    }

    public double getPrice() {
        return this.price;
    }

    public void setPrice(double price) {
        this.price = Math.abs(price);
    }

    public double purchase(int quantity) {
        return this.price * quantity;
    }

    public String toString() {
        return String.format("%s (%s): $%.2f", this.name,
            this.sku, this.price);
    }

    public boolean equals(Item other) {
        return (this.name.equals(other.name) &&
            this.sku.equals(other.sku) &&
            this.price == other.price);
    }
}
Listing A.2.12.
Let’s start on the code for SaleItem. You might be tempted to write code like Listing A.2.13:
public class SaleItem extends Item {
    private double discount; // as a decimal

    public SaleItem(String name, String sku, double price,
       double discount)  {
       this.name = name;
       this.sku = sku;
       this.price = Math.abs(price);
       this.discount = Math.max(0,
           Math.min(discount, 1.00));
    }
}
Listing A.2.13. Incorrect Code for SaleItem Constructor
But that won’t work, because name, price, and sku are private to the Item class. We can’t call the setter methods for name and sku because there aren’t any. What we need to do is call the superclass constructor, which can set those attributes. To call a superclass constructor, you use the keyword super:
public class SaleItem extends Item {
    private double discount; // as a decimal

    public SaleItem(String name, String sku, double price,
       double discount)  {
       super(name, sku, price);
       this.discount = Math.max(0,
           Math.min(discount, 1.00));
    }
}
Listing A.2.14. Using super in a Constructor
Important: When calling a superclass constructor, the call to super must be the first non-comment line in the subclass’s constructor.
Here’s the rest of the SaleItem code:
    public double getDiscount() {
        return this.discount;
    }

    public void setDiscount(double discount) {
        this.discount =
            Math.max(0, Math.min(discount, 1.00));
    }

    public double purchase(int quantity) {
        return (this.getPrice() * quantity) *
            (1 - discount);
    }

    public String toString() {
        return String.format("%s (%s): $%.2f - %.1f%% discount",
            this.getName(), this.getSku(), this.getPrice(),
            this.discount * 100.0);
    }

    public boolean equals(SaleItem other) {
        return (this.getName().equals(other.getName()) &&
            this.getSku().equals(other.getSku()) &&
            this.getPrice() == other.getPrice() &&
            this.discount == other.discount);
    }
}
Listing A.2.15.
Again, because name, sku, and price are private to the Item class, the purchase, toString and equals methods must use the getter methods to access those private values.
Listing A.2.16 creates an Item and a SaleItem, prints them, and then purchases ten of each:
public class ItemTest {

    public static void main(String[] args) {
        Item envelopes = new Item(
            "Letter Size Envelopes - 100 count",
            "LSE-0100", 5.75);
        SaleItem marker = new SaleItem(
            "Erasable marker - black", "EMB-913",
            2.15, 0.10);

        System.out.println(envelopes);
        double envelopeTotal = envelopes.purchase(10);
        System.out.printf("Ten boxes of envelopes cost $%.2f\n",
            envelopeTotal);

        System.out.println(marker);
        double markerTotal = marker.purchase(10);
        System.out.printf("Ten markers cost $%.2f\n",
            markerTotal);
    }
}
Listing A.2.16. Testing the Item and SaleItem Classes
When we run the program, we get the correct output:
Letter Size Envelopes - 100 count (LSE-0100): $5.75
Ten boxes of envelopes cost $57.50
Erasable marker - black (EMB-913): $2.15 - 10.0% discount
Ten markers cost $19.35
Congratulations! We’ve used inheritance, and our program works. Now it’s time to do what Joe Armstrong said in his book Erlang and OTP in Action: “Make it work, then make it beautiful, then if you really, really have to, make it fast. 90 percent of the time, if you make it beautiful, it will already be fast. So really, just make it beautiful!”
Part of making a program beautiful is getting rid of unnecessary duplication. In that spirit, let’s take a closer look at the SaleItem class’s purchase, toString and equals methods. The calculation of the base price in purchase is the same as in Item. The part of toString’s format string for the name, sku, and price is the same as in Item. Similarly, the first three lines of equals are the same as in Item.
We can once again use the super keyword to call methods in a parent class. Listing A.2.17 is a rewrite that eliminates unnecessary duplication:
public double purchase(int quantity) {
    return super.purchase(quantity) *
        (1 - discount / 100.0);
}

public String toString() {
    return String.format("%s - %.1f%% discount",
        super.toString(),
        this.discount * 100.0);
}

public boolean equals(SaleItem other) {
    return (super.equals(other) &&
        this.discount == other.discount);
}
Listing A.2.17.

Exercises Exercises

1.
Design a class named Person with two subclasses: Employee and Customer. The attributes for these classes are described in italics. A Person has a name, address, phone number, and email address.
An Employee has an employee number, hire date, and salary.
The Employee class, in turn, has three subclasses: Programmer, Tester, and Manager.
  • A Programmer and a Tester have a cubicle number. Both will receive a fixed bonus at the end of the year.
  • A Manager has an office number and has a variable bonus based on the performance of their team. This means that a Manager should have attributes for the target bonus amount and the performance percentage.
Finally, the Customer should have a customer number and company they work for.
Draw the UML diagram showing the relationship of these classes, then code all these classes showing the data fields and attributes. Make meaningful names for the attributes and give them an appropriate data type. (You do not need to create constructors or other methods for the classes.)
2.
The XYZZY Corporation wants to retain their most loyal customers. They launch a customer retention program and offer discount to customers who have been purchasing from the company for at least one year.
Write a subclass PreferredCustomer that extends the Customer class from the preceding exercise. The PrefCust class should have two data fields: average annual purchase amount (average dollar amount purchased per year) and years (number of years they have been a customer). These are both private variables.
Customers get a discount percentage based on their years as a customer and average purchase amount. There are three levels of Preferred Customers: bronze, silver, and gold.
  • Bronze: \(\geq\) 1 year and average purchase amount \(\geq\) $5000 per year—5% discount
  • Silver: \(\geq\) 2 years and average purchase amount \(\geq\) $10000 per year—7.5% discount
  • Gold: \(\geq\) 3 years and average purchase amount \(\geq\) $15000 per year—10% discount
The discount percentage is a derived attribute—it is never set directly, but instead is computed based on other attributes.
Write the PrefCust class with all its data fields. Please write all the getter and setter methods. Write a method named getDiscount that uses the average annual purchase amount and years as a customer to return the discount percent (as a percentage).
3.
In this exercise, you will implement an Account class which represents a bank checking account. You will then create two classes that inherit from Account: SavingsAccount and CreditCardAccount.
You will then use composition to create a Customer class which includes instances of each of these account classes. Finally, you will write a program with a main method that works with these classes.
Part 1: Create a class named Account, which has the following private properties:
  • number: long
  • balance: double
Here are the constructors and other methods:
  • Create a two-parameter constructor that takes an account number and balance. Make sure that the balance is always greater than zero (Hint: Math.abs)
  • Implement getters and setters: getNumber(), getBalance(), and \newline setBalance(double newBalance). There is no setNumber method—once an account is created, its account number cannot change.
  • Implement these methods: void deposit(double amount) and \newline void withdraw(double amount). For both these methods, if the amount is less than zero, the account balance remains untouched. For the withdraw method, if the amount is greater than the balance, it remains untouched. These methods do not print anything.
  • Implement a toString method that returns a string with the account number and balance, properly labeled.
Part 2: Next, implement the SavingsAccount class. It inherits from Account and adds a private apr property, which is the annual percentage rate (APR) for interest.
  • Write a three-argument constructor that takes an account number, balance, and interest rate as a decimal (thus, a 3.5% interest rate is given as 0.035). Make sure that the interest rate is never less than zero.
  • Add a getter and setter: getApr() and setApr(double apr). The setter must ensure that the APR is never less than zero.
  • Write a calculateInterest instance method that returns the annual interest, calculated as the current balance times the annual interest rate.
  • Modify toString to include the interest rate. IMPORTANT: The value returned by the toString method must not include the calculated annual interest.
Part 3: Next, implement the CreditCardAccount class, which inherits from Account and adds these private properties:
  • apr, a double representing the annual interest rate charged on the balance.
  • creditLimit, a double which gives the credit limit for the card.
Then, implement the following:
  • A four-argument constructor that takes an account number, balance, interest rate as a decimal (thus, a 3.5% interest rate is given as 0.035), and credit limit. Make sure that neither the interest rate nor credit limit can be negative.
  • Write getters and setters for the apr and creditLimit. The apr setter should leave the APR untouched if given a negative value. The creditLimit setter should leave the credit limit untouched if given a negative value.
  • Modify toString to include the interest rate and credit limit. IMPORTANT: the value returned by the toString method must not include the monthly payment.
  • Override the withdraw method so that you can have a negative balance. If a withdrawal would push you over the credit limit, leave the balance untouched. Examples:
    • If your balance is $300 with a credit limit of $700, you can withdraw $900 (leaving a balance of $-600).
    • If your balance is $-300 with a credit limit of $700, you can withdraw $350 (leaving a balance of $-650).
    • If your balance is $-300 with a credit limit of $700, you can not withdraw $500, because that would then owe $800, which is more than your $700 limit.
    In short, the maximum amount you can withdraw is your current balance plus the credit limit.
  • Implement a calculatePayment method that works as follows: If the balance is positive, the minimum amount you have to pay on your card per month is zero. Otherwise, your monthly payment is the minimum of 20 and $(apr/12) \cdot (−balance)$
Part 4: Now, write a Customer class that will use composition to include the preceding classes.
  • The Customer class has the following private attributes:
    • name: String
    • acct: Account
    • savings: SavingsAccount
    • credit: CreditAccount
  • Implement a four-argument constructor for this class.
  • Write getters and setters for all the fields.
Figure A.2.18 shows the details of the Account class and its subclasses. Figure A.2.19 shows the relationships of all the classes in this exercise.
Figure A.2.18. Account, SavingsAccount, and CreditCardAccount classes
Figure A.2.19. Composition and Inheritance Among All Classes
Part 5: Finally, write a program named TestCustomer.java that creates a Customer named “Joe Doakes” with this data:
  • Regular account number 1037825 with a balance of $3,723.00
  • Savings account number 9016632 with a balance of $4,810.25 and an annual interest rate of 2.05%
  • Checking account number 85332162 with a balance of -$2500.00, an interest rate of 7.125%, and a credit limit of $3000.00.
Then, do the following transactions:
  • Deposit $257.18 into the regular account, then withdraw $587.23.
  • Deposit $2,466.12 into the savings account, then withdraw $8,000.00.
  • Withdraw $480.00 from the credit card account.
  • Display the status of the regular account (number and balance).
  • Display the status of the savings account (number, balance, and annual interest amount).
  • Display the status of the credit card account (number, balance, interest rate, and monthly payment due).
You have attempted of activities on this page.