Chapter 9
Interfaces and Inheritance

Copyright © 2004 by Stuart Reges and Marty Stepp

9.1 Introduction

In this chapter we will explore two of the most important techniques provided by the Java language to write better structured solutions. Interfaces allow us to treat several different classes of objects as if they were the same while inheritance allows us to structure our classes so that we don't have to write the same code multiple times.

9.2 Interfaces

Consider the Stock class example from the last chapter. Stocks are not the only type of financial asset that investors might have in their financial portfolios. Other investments might include mutual funds, real estate, or cash. Though each of these types of assets is unique, they do have common characteristics. Most importantly, each has a certain current market value.

If we were writing additional code to manage many types of financial assets, we might write classes like MutualFund, Cash, and so on. Perhaps we'd want to store a person's collection of assets in an array or list. But arrays can only hold elements of the same type, so we would need one array for Stocks, one for MutualFunds, and so on. This is unnecessarily bulky and inconvenient. Also, our code to examine and manage the finances would have to treat each type of asset separately, and it could not take advantage of any similarity between the types of assets.

It would be convenient to be able to treat any asset the same way, insofar as they share similar functionality. For example, every asset has a market value, so it would be nice to be able to put all a person's assets into one large array, and then to be able to ask any asset for its market value, without worrying about exactly which type of asset it is.

One hurdle is that different assets compute their market value in different ways. Stocks' values are based on the current share price, while the value of cash depends only on how much cash the person has.

Java has a construct called an interface that can represent a common ground between several classes. Think of an interface as a set of qualifications that can be imposed on a class. We could write an interface to represent the common functionality of all financial assets--the ability to ask for their market value, and the ability to ask for the amount of profit or loss on the asset.

Interface

A set of methods that classes can promise to implement, allowing those classes to be treated similarly in your code.

Asking for an asset's market value is achieved by calling its getMarketValue method, and asking for an asset's profit or loss is achieved by calling its getProfitLoss method, so our interface will demand that all assets have these methods. The interface is a way of saying, "classes that want to consider themselves assets must have a getMarketValue and getProfitLoss method."

The code for an interface looks like a class, but it can only contain methods' names, along with their parameters and return types. Interfaces don't specify how their methods are implemented--only the names and signatures of the methods. Our interface for financial assets would be saved into a file named Asset.java and might look like this:

public interface Asset { public double getMarketValue(); public double getProfitLoss(); } Now, classes can "certify" that they are assets, by declaring that they obey the interface's requirements and by implementing the two methods. A class indicates that it is qualified to be an Asset by writing "implements Asset" in its class header, just after the class name:

    public class Stock implements Asset {
        ...
    }
For example, here is a version of the Cash class that implements this interface.

// Represents an amount of money held by an investor. public class Cash implements Asset { private double myAmount; // Constructs a new empty amount of cash, representing $0.00. public Cash() { this(0.00); } // Constructs an amount of cash equal to the given amount of dollars. public Cash(double amount) { myAmount = amount; } // Returns the amount of cash held. public double getMarketValue() { return myAmount; } // Returns the profit or loss on this cash--since cash is a // fixed asset, it never has any profit or loss. public double getProfitLoss() { return 0.00; } // Sets the amount of cash held to the given amount. public void setAmount(double amount) { myAmount = amount; } // Returns a string representation of this Cash. public String toString() { String result = "Cash ( $ " + myAmount; if (myAmount * 100 % 10 == 0) result += "0"; result += " )"; return result; } } If you declare that your class implements an interface, your class must now have an implementation of every method in that interface, or else it will not compile. For example, if we try to lie and certify a class as an Asset that doesn't have a way to get its market value or profit/loss, the compiler will complain:

    public class EmptyClass implements Asset {
        // How incomplete I am!
    }
The compiler would give this error when trying to compile EmptyClass.java:

    C:\document\EmptyClass.java:1: EmptyClass is not abstract and does not
    override abstract method getMarketValue() in Asset
    public class EmptyClass implements Asset {
           ^

    C:\document\EmptyClass.java:1: EmptyClass is not abstract and does not
    override abstract method getProfitLoss() in Asset
    public class EmptyClass implements Asset {
           ^
    2 errors
The error message is a bit cryptic, but the gist of it is that the compiler is complaining because EmptyClass objects don't have a getMarketValue or getProfitLoss method, so they cannot be assets. Our Stock class wouldn't have this error, because it does implement those two methods for its objects:

    public class Stock implements Asset {
        ...

        public double getMarketValue() {
            return myNumShares * myCurrentPrice;
        }

        public double getProfitLoss() {
    	    return getMarketValue() - myTotalCost;
        }

        ...
    }
The general syntax for declaring an interface is the following:

public interface <name> { public <type> <name>(<type> <name>, ..., <type> <name>); public <type> <name>(<type> <name>, ..., <type> <name>); ... public <type> <name>(<type> <name>, ..., <type> <name>); } And the general syntax for implementing an interface in a class is the following:

public class <name> implements <name> { ... } It's important to understand that an interface isn't a new type like a class is. It isn't legal to try to create objects of an interface. For example, this code:

    Asset a = new Asset();
leads to the following compiler error message:

    C:\document\StockManager.java:29: Asset is abstract; cannot be instantiated
        Asset a = new Asset();

9.3 Polymorphism

The key benefit of interfaces is that they let us treat related classes in the same way. Even though we can't create objects of type Asset, we can create arrays or variables of type Asset. Such an array or variable can refer to any object that implements the Asset interface.

    Asset[] investments = new Asset[4];
    investments[0] = new Stock("MSFT", 27.50, 10, 300.00);
    investments[1] = new Cash(500.00);
    investments[2] = new Cash(1200.54);
    investments[3] = new Stock("INTC", 23.00, 30, 620.00);

    for (int i = 0; i < investments.length; i++) {
        System.out.println(investments[i]);
        System.out.print("Market value is: " + investments[i].getMarketValue());
        System.out.println(", profit/loss is: " + investments[i].getProfitLoss());
        System.out.println();
    }
This code produces the following output:

MSFT ( 10 shares, $ 300.00 total cost, $ 27.50 current price ) Market value is: 275.0, profit/loss is: -25.0 Cash ( $ 500.00 ) Market value is: 500.0, profit/loss is: 0.0 Cash ( $ 1200.54 ) Market value is: 1200.54, profit/loss is: 0.0 INTC ( 30 shares, $ 620.00 total cost, $ 23.00 current price ) Market value is: 690.0, profit/loss is: 70.0 This deceptively simple code is actually doing some very powerful things. For starters, we are able to create an array that holds objects of two different types. Second, our for loop is able to print each asset in the array (using its toString method), as well as ask for its market value.

Keep in mind that the actual toString or getMarketValue method called could be one of two choices: the version in the Stock class or the version in the Cash class. Java does the right thing and calls the correct method, depending on what kind of asset is stored in each index of the array. Our for loop didn't have to check which type of asset was in each index; it was able to treat all the assets in the same way.

The ability for the same code to be used with several different types of objects is called polymorphism. Polymorphism is useful because it allows us to implement code that is more general and works correctly with a broader range of objects. In Java, one way to achieve polymorphism is by using interfaces.

Polymorphism

The ability for the same code to be used with several different types of objects, possibly behaving differently depending on the type of objects used.

9.4 Inheritance

Sometimes we wish to create a new class that is similar to an existing class. Consider the example of creating a class to represent ownership of stocks that pay dividends. We don't want to add dividend code to our existing Stock class, because not all stocks have dividend payments. But the code for such a class would look very similar to that of the Stock class. Here is a possible implementation:

    public class DividendStock implements Asset {
        private int myNumShares;
        private double myTotalCost;
        private double myCurrentPrice;
        private String mySymbol;
        private double myDividends;

        public DividendStock(String symbol, double currentPrice) {
            mySymbol = symbol;
            myCurrentPrice = currentPrice;
            myNumShares = 0;
            myTotalCost = 0.0;
            myDividends = 0.0;
        }

        public double getCurrentPrice() {
            return myCurrentPrice;
        }

        public double getDividends() {
            return myDividends;
        }

        public double getMarketValue() {
            return myNumShares * myCurrentPrice;
        }

        public int getNumShares() {
            return myNumShares;
        }

        public double getProfitLoss() {
            return myDividends + getMarketValue() - myTotalCost;
        }

        public String getSymbol() {
            return mySymbol;
        }

        public double getTotalCost() {
            return myTotalCost;
        }

        public void payDividend(double amount) {
            myDividends += amount;
        }

        public void recordPurchase(int numShares, double pricePerShare) {
            myNumShares += numShares;
            myTotalCost += numShares * pricePerShare;
        }

        public void setCurrentPrice(double currentPrice) {
            myCurrentPrice = currentPrice;
        }

        public String toString() {
            String result = getSymbol();
            result += " ( " + myNumShares + " shares";

            result += ", $ " + myTotalCost;
            if (myTotalCost * 100 % 10 == 0)
                result += "0";
            result += " total cost";

            result += ", $ " + myCurrentPrice;
            if (myCurrentPrice * 100 % 10 == 0)
                result += "0";
            result += " current price )";

            return result;
        }
    }
The code for DividendStock is extremely redundant compared to that of the Stock class, and we'd like to get rid of this redundancy. Also, dividend-paying stocks really are a subtype of stocks in general, so it would be nice to be able to represent this relationship in some way in the code.

Java provides a mechanism called inheritance that can help us remove redundant code like this. Inheritance allows the programmer to specify a parent-child relationship between two classes.

Inheritance

A parent-child relationship between two classes, where objects of the child class receive their own copy of the data fields and methods of the parent class.

The child, or subclass, receives all of the state and behavior of its parent, or superclass. The child can then add existing state and behavior of its own, or it can replace its inherited behavior with new behavior as needed.

Superclass or Base Class

The parent in an inheritance relationship. A class can only have one superclass.

Subclass or Derived Class

The child in an inheritance relationship.

In our case, we'd really like DividendStock to inherit all of the behavior from Stock, but then add new behavior for dividends. The only existing behavior from Stock that must change in DividendStock is the implementation of getProfitLoss, which now depends on the total dividends paid.

To specify that DividendStock is a subclass of Stock, and that it should receive all of the data fields and methods from the Stock class, we write "extends Stock" in DividendStock's class header:

    public class DividendStock extends Stock {
      ...
    }
One we've done this, it means that DividendStock now has a myCurrentPrice, mySymbol, and myTotalCost field, and it now has methods named getCurrentPrice, getMarketValue, getNumShares, getProfitLoss, getSymbol, getTotalCost, recordPurchase, setCurrentPrice, and toString. We say that DividendStock "inherits" these things from its superclass, Stock. The main thing left to do to DividendStock is to add the code to handle the dividends.

The general syntax to specify one class as the child (subclass) of another is the following:

public class <name> extends <name> { ... } A Java class can have only one parent; it is not possible to extend more than one class. This is called single inheritance.

9.4.1 Private Access and the super Keyword

One quirk of inheritance is that, while data fields and methods are inherited from a superclass into its subclass(es), constructors are not. This means that our DividendStock class must have a constructor of its own. We'd like to make our constructor set the mySymbol and myCurrentPrice fields, just like the Stock constructor does, but it is illegal to do so:

    public class DividendStock extends Stock {
        private double myDividends;

        public DividendStock(String symbol, double currentPrice) {
            mySymbol = symbol;              // This code does not work!
            myCurrentPrice = currentPrice;  // This code does not work!
            myDividends = 0.0;
        }
Because the mySymbol and myCurrentPrice fields are private in the Stock class, DividendStock can't see them. The following compiler errors result from trying to compile the previous code:

    C:\document\DividendStock.java:5: mySymbol has private access in Stock
          mySymbol = symbol;
          ^
    C:\document\DividendStock.java:6: myCurrentPrice has private access in Stock
          myCurrentPrice = currentPrice;
          ^
    2 errors
The only easy way to set the symbol and current price to their initial values is to have the DividendStock constructor call the constructor from the Stock class, and let it set the values. To do this, we use the "super" keyword. The "super" keyword refers to our current class's superclass, and it allows us to refer to a constructor, data field or method from the superclass.

To call the superclass's constructor, we write super, followed by the required arguments in parentheses.

    public DividendStock(String symbol, double currentPrice) {
        super(symbol, currentPrice);
        myDividends = 0.0;
    }
The general syntax for using the super keyword to refer to a constructor, method, or field is the following:

super(<name>, ..., <name>); // constructor super.<name>(<name>, ..., <name>); // method super.<name> // field

9.4.2 Overriding Behavior in a Subclass

A DividendStock should include dividend payments as profit when computing its profit/loss. This means that DividendStock does not want to inherit the behavior from Stock, which only takes into account the total cost and market value. We can write a new version of getProfitLoss for DividendStock to replace the version that was inherited from Stock.

    public double getProfitLoss() {
        return myDividends + getMarketValue() - getTotalCost();
    }
This is called overriding.

Override

Implementing a new version of a method inherited from a superclass, replacing the superclass's version.

If we really want to reduce redundancy, we can notice that our new getProfitLoss still computes the old profit/loss value (market value - total cost) and adds to it. Therefore, we can use the super keyword to call the old getProfitLoss method's value, then add the dividends to it to get the new profit/loss:

    public double getProfitLoss() {
        return myDividends + super.getProfitLoss();
    }
After implementing these changes, our DividendStock class looks like this. We don't need to say implements Asset again in the header for DividendStock, because this is already covered by the superclass.

public class DividendStock extends Stock { private double myDividends; public DividendStock(String symbol, double currentPrice) { super(symbol, currentPrice); myDividends = 0.0; } public double getDividends() { return myDividends; } public double getProfitLoss() { return myDividends + super.getProfitLoss(); } public void payDividend(double amount) { myDividends += amount; } }

9.5 Abstract Classes

If inheritance lets us specify classes as parents and children, interfaces may be like distant cousins: related, but without many details in common. There are situations where we want more of a sibling relationship--classes with common method names, like we get with interfaces, but also with some code shared between them, like we get with inheritance.

Let's consider the case where we want to write a third asset class to represent mutual funds. A mutual fund is a shared investment plan where investors place money into the hands of an investment firm. The firm then invests the money into various stocks and assets, and the profits are shared among the investors.

Mutual funds aren't really a subcategory of stocks, so it wouldn't be right to make a MutualFund class that extends Stock. Mutual funds are more like siblings of stocks--they have a lot of behavior and state in common, but not all. One major difference between a mutual fund and a stock is that it is possible to own partial shares of mutual funds. We'll represent this in our MutualFund class by making the number of shares a data field of type double, rather than int.

Here is an implementation of MutualFund that can be improved upon:

    // A Stock object represents an investor's purchases
    // of shares of a particular Mutual Fund.
    public class MutualFund implements Asset {
        // data fields
        private double myNumShares;  // partial shares are allowed
        private double myTotalCost;
        private double myCurrentPrice;
        private String mySymbol;

        // constructor
        // Creates a new Mutual Fund with the given symbol
        // and the given current price per share.
        public MutualFund(String symbol, double currentPrice) {
            mySymbol = symbol;
            myCurrentPrice = currentPrice;
            myNumShares = 0;
            myTotalCost = 0.0;
        }

        // methods
        // Returns the value of all shares if they were purchased at today's price.
        public double getMarketValue() {
            return myNumShares * myCurrentPrice;
        }

        // Returns this Mutual Fund's number of shares purchased.
        public double getNumShares() {
            return myNumShares;
        }

        // Returns total profit or loss earned on shares of this Mutual Fund.
        public double getProfitLoss() {
            return getMarketValue() - myTotalCost;
        }

        // Records a purchase of the given number of shares of this Mutual Fund
        // at the given price per share.
        public void recordPurchase(double numShares, double pricePerShare) {
            myNumShares += numShares;
            myTotalCost += numShares * pricePerShare;
        }

        // Sets today's price per share of this Mutual Fund to be the given amount.
        public void setCurrentPrice(double currentPrice) {
            myCurrentPrice = currentPrice;
        }
    }
One thing you'll notice is that the code for the MutualFund class is very similar to that of the Stock class. They aren't identical, but they share so much common code that we'd like to get rid of the redundancy somehow. Making MutualFund extend Stock would be wrong for a few reasons, mainly that it's illegal for us to change the type of something we override, which we'd have to do if we changed getNumShares to return type double rather than int.

MutualFunds aren't a subclass of Stocks, but they do have things in common that we can represent by giving the two a common parent class. We can create a third class that represents everything that stocks and mutual funds have in common. This class will become the parent for both Stock and MutualFund. We'll call it ShareAsset, because both stocks and mutual funds are assets divided into shares. The common code between Stock and MutualFund is the code for the symbol, total cost, and current price. Notice that we have to remove anything that refers to number of shares, because this is implemented differently by each of Stock and MutualFund.

    public class ShareAsset {
        private double myTotalCost;
        private double myCurrentPrice;
        private String mySymbol;

        public ShareAsset(String symbol, double currentPrice) {
            mySymbol = symbol;
            myCurrentPrice = currentPrice;
        }

        public double getCurrentPrice() {
            return myCurrentPrice;
        }

        public String getSymbol() {
            return mySymbol;
        }

        public double getTotalCost() {
            return myTotalCost;
        }

        public void setCurrentPrice(double currentPrice) {
            myCurrentPrice = currentPrice;
        }
    }
A ShareAsset isn't really a type of asset that a person can buy; it's just a fictional idea created in our code. If we want to create classes like this that shouldn't be used directly by outside code, but should only serve as a superclass for inheritance, we can call the class abstract. Writing "abstract" in the class's header will specify that a class is abstract and cannot be used directly to construct objects.

    public abstract class ShareAsset implements Asset {
        ...
    }
An attempt to create a ShareAsset object will produce this compiler error, if ShareAsset was declared to be abstract:

    C:\document\StockManager.java:55: ShareAsset is abstract; cannot be instantiated
          ShareAsset asset = new ShareAsset("MSFT", 27.46);
          ^
    1 error
Abstract classes can also behave like interfaces, in that they can demand behavior be implemented by any of their subclasses. Such required subclass behaviors are called abstract methods. One improvement we can make to our ShareAsset class is to demand that any subclass implement a getMarketValue method, because each will need this to implement the Asset interface. To make such a demand, we declare an abstract getMarketValue method:

  public abstract double getMarketValue();
An abstract method is declared with a header similar to regular methods, but with the keyword "abstract" after the public modifier. The other difference about abstract methods is that they do not have method bodies--they only have a semicolon, which means that the behavior is specified in each subclass. The general syntax for abstract method declarations is the following:

public abstract <type> <name>(<type> <name>, ..., <type> <name>); We can't declare getNumShares as abstract, because it has a different return type in Stock and in MutualFund.

Because an abstract class can contain normal data fields and methods with implementation, and it can also contain abstract methods without implementation, abstract classes can be thought of as hybrids between Java classes and interfaces. One important difference between interfaces and abstract classes is that a class may choose to implement arbitrarily many interfaces, but it can only extend one abstract class.

Abstract Class

A Java class that serves only as a superclass-and-interface for other classes, not actually to create objects of itself.

If we make our ShareAsset class abstract, it might look like the following:

public abstract class ShareAsset implements Asset { private double myTotalCost; private double myCurrentPrice; private String mySymbol; public ShareAsset(String symbol, double currentPrice) { mySymbol = symbol; myCurrentPrice = currentPrice; } public void addCost(double cost) { myTotalCost += cost; } public double getCurrentPrice() { return myCurrentPrice; } public double getProfitLoss() { return getMarketValue() - getTotalCost(); } public String getSymbol() { return mySymbol; } public double getTotalCost() { return myTotalCost; } public void setCurrentPrice(double currentPrice) { myCurrentPrice = currentPrice; } public abstract double getMarketValue(); } Now we can modify the Stock class to take advantage of ShareAsset. Notice that we must implement getMarketValue, or else we receive an error.

    public class Stock extends ShareAsset {
        private int myNumShares;

        public Stock(String symbol, double currentPrice) {
            super(symbol, currentPrice);
            myNumShares = 0;
        }

        public double getMarketValue() {
            return myNumShares * getCurrentPrice();
        }

        public int getNumShares() {
            return myNumShares;
        }

        public void recordPurchase(int numShares, double pricePerShare) {
            myNumShares += numShares;
            addCost(numShares * pricePerShare);
        }
    }
MutualFund gets a similar makeover:

public class MutualFund extends ShareAsset { private double myNumShares; public MutualFund(String symbol, double currentPrice) { super(symbol, currentPrice); myNumShares = 0.0; } public double getMarketValue() { return myNumShares * getCurrentPrice(); } public double getNumShares() { return myNumShares; } public void recordPurchase(double numShares, double pricePerShare) { myNumShares += numShares; addCost(numShares * pricePerShare); } }
Stuart Reges
Last modified: Fri Nov 19 15:59:23 PST 2004