520 likes | 802 Views
Chapter 10. Object-Oriented Programming Part 3: Inheritance, Polymorphism, and Interfaces. Topics. Inheritance Concepts Inheritance Design Inherited Members of a Class Subclass Constructors Adding Specialization to the Subclass Overriding Inherited Methods
E N D
Chapter 10 Object-Oriented Programming Part 3: Inheritance, Polymorphism, and Interfaces
Topics • Inheritance Concepts • Inheritance Design • Inherited Members of a Class • Subclass Constructors • Adding Specialization to the Subclass • Overriding Inherited Methods • The protected Access Modifier
Inheritance Concepts • A common form of reuse of classes is inheritance. • We can organize classes into hierarchies of functionality. • The class at the top of the hierarchy (superclass) defines instance variables and methods common to all classes in the hierarchy. • We derive a subclass, which inherits behavior and fields from the superclass.
Inheritance • Inheritance allows a software developer to derive a new class from an existing one • The existing class is called the parent class, or superclass, or base class • The derived class is called the child class or subclass • As the name implies, the child inherits characteristics of the parent • That is, the child class inherits the methods and data defined by the parent class
A Sample Vehicle Hierarchy • This hierarchy is depicted using a Unified Modeling Language (UML) diagram. • In UML diagrams, arrows point from the subclass to the superclass.
Superclasses and Subclasses • A superclass can have multiple subclasses. • Subclasses can be superclasses of other subclasses. • A subclass can inherit directly from only one superclass. • All classes inherit from the Object class.
Superclasses and Subclasses • A big advantage of inheritance is that we can write common code once and reuse it in subclasses. • A subclass can define new methods and instance variables, some of which may override (hide) those of a superclass.
Specifying Inheritance • The syntax for defining a subclass is to use the extends keyword in the class header, as in accessModifier class SubclassName extends SuperclassName { // class definition } • The superclass name specified after the extends keyword is called the direct superclass. • As mentioned, a subclass can have many superclasses, but only one direct superclass.
Business RetailBusiness ServiceBusiness KMart Macys Kinkos Class Hierarchies • A child class of one parent can be the parent of another child, forming a class hierarchy
Class Hierarchies • Two children of the same parent are called siblings • Common features should be put as high in the hierarchy as is reasonable • An inherited member is passed continually down the line • Therefore, a child class inherits from all its ancestor classes • There is no single class hierarchy that is appropriate for all situations
The Object Class • A class called Object is defined in the java.lang package of the Java standard class library • All classes are derived from the Object class • If a class is not explicitly defined to be the child of an existing class, it is assumed to be the child of the Object class • Therefore, the Object class is the ultimate root of all class hierarchies
The Object Class • The Object class contains a few useful methods, which are inherited by all classes • For example, the toString method is defined in the Object class • Every time we define the toString method, we are actually overriding an inherited definition • The toString method in the Object class is defined to return a string that contains the name of the object’s class along with some other information
Example public class ObjectExample {public static void main( String [] args ){ Object o1 = new Object(); System.out.println("This is an object " + o1); Temp t = new Temp(); System.out.println("This is a temp object " + t); } } public class Temp{ private int a; private char ch;} Output ----jGRASP exec: java ObjectExampleThis is an object java.lang.Object@10b62c9This is a temp object Temp@923e30
The Object Class • The equals method of the Object class returns true if two references are aliases • We can override equals in any class to define equality in some more appropriate way • As we've seen, the String class defines the equals method to return true if two String objects contain the same characters • The designers of the String class have overridden the equals method inherited from Object in favor of a more useful version
An Applet Hierarchy • When we wrote an applet,we defined a subclass. • We say that inheritance implements an "is a" relationship, in that a subclassobject "is a" superclass object as well. • Thus, RollABall "is a" JApplet (its direct superclass), Applet, Panel, Container,Component, andObject. • RollABall begins with more than 275 methods and 15 fields inheritedfrom its 6 superclasses.
The Bank Account Hierarchy • The BankAccount class isthe superclass. • Instance variables: • balance (double) • MONEY (a constantDecimalFormat object) • Methods: • Default and overloaded constructors • deposit and withdraw methods • balance accessor • toString
Example 10.1 public class BankAccount { public final DecimalFormat MONEY = new DecimalFormat( "$#,##0.00" ); private double balance; /** Default constructor sets balance to 0.0 */ public BankAccount( ) { balance = 0.0; } /** Overloaded constructor @param startBalance beginning balance */ public BankAccount( double startBalance ) { deposit( startBalance ); } /** Accessor for balance @return current account balance */ public double getBalance( ) { return balance; } /** toString @return the balance formatted as money */ public String toString( ) { return ( "balance is " + MONEY.format( balance ) ); }
/** Deposit amount to account @param amount amount to deposit; * amount must be >= 0.0 */ public void deposit( double amount ) { if ( amount >= 0.0 ) balance += amount; else System.err.println( "Deposit amount must be positive." ); } /** withdraw amount from account * @param amount amount to withdraw; amount must be >= 0.0 * amount must be <= balance */ public void withdraw( double amount ) { if ( amount >= 0.0 && amount <= balance ) balance -= amount; else System.err.println( "Withdrawal amount must be positive " + "and cannot be greater than balance" ); } }
The CheckingAccount Class • We derive the CheckingAccount subclass from BankAccount: public class CheckingAccount extends BankAccount { } • Notice that nothing is defined in the class definition, but we can still instantiate and use an instance • A subclass inherits all the public members of a superclass. Thus, the CheckingAccount class inherits • the MONEY instance variable • The getBalance, deposit, withdraw, and toString methods
Example 10.3 ----jGRASP exec: java CheckingAccountClientNew checking account: balance is $0.00After depositing $350.75: balance is $350.75After withdrawing $200.25: balance is $150.50 public class CheckingAccountClient { public static void main( String [] args ) { CheckingAccount c1 = new CheckingAccount( ); System.out.println( "New checking account: " + c1 ); c1.deposit( 350.75 ); System.out.println( "\nAfter depositing $350.75: " + c1 ); c1.withdraw( 200.25 ); System.out.println( "\nAfter withdrawing $200.25: " + c1 ); } }
private Members • Superclass members declared as private are part of the subclass, but not accessible by the subclass • Thus, the balance instance variable is allocated to all CheckingAccount objects, but methods of the CheckingAccount class cannot directly access balance. • To set or get the value of balance, the CheckingAccount methods must call the withdraw, deposit, or getBalance methods. • This simplifies maintenance because the BankAccount class enforces the data validation rules for balance.
Subclass Constructors • Constructors are not inherited. • However, the subclass can call the constructors of the superclass to initialize inherited fields. • Implicit invocation • The default constructor of the subclass automatically calls the default constructor of the superclass • For explicit invocation, use this syntax: super( argument list ); If used, this statement must be the first statement in the subclass constructor
The super Reference • A child’s constructor is responsible for calling the parent’s constructor • The first line of a child’s constructor should use the super reference to call the parent’s constructor • The super reference can also be used to reference other variables and methods defined in the parent’s class
CheckingAccount Constructors public CheckingAccount( ) { // optional explicit call // to BankAccount default constructor super( ); } public CheckingAccount( double startBalance ) { // explicit call to BankAccount // overloaded constructor super( startBalance ); }
Common ErrorTrap • An attempt by a subclass to directly access a private field or call a private method defined in a superclass will generate a compiler error. • To set initial values for private variables, call the appropriate constructor of the direct superclass. • For example, this statement in the overloaded CheckingAccount class constructor calls the overloaded constructor of the BankAccount class: super( startBalance );
Software Engineering Tip Overloaded constructorsin a subclass should explicitly call the direct superclass constructor to initialize the fields in its superclasses.
Adding Specialization • A subclass can define new fields and methods. • Our CheckingAccount class adds • these instance variables: • monthlyFee, a double • DEFAULT_FEE, a double constant • these methods: • setMonthlyFee, the accessor • getMonthlyFee, the mutator • applyMonthlyFee, which charges the monthly fee to the account.
The applyMonthlyFee Method • Because balance is private in the BankAccount class, the applyMonthlyFee method calls the withdraw method to subtract the monthly fee from the balance: public void applyMonthlyFee( ) { withdraw( monthlyFee ); }
Example 10.7 public class CheckingAccount extends BankAccount { public final double DEFAULT_FEE = 5.00; private double monthlyFee; /** default constructor explicitly calls the BankAccount default constructor * set monthlyFee to default value */ public CheckingAccount( ) { super( ); // optional monthlyFee = DEFAULT_FEE; } /** overloaded constructor calls BankAccount overloaded constructor * @param startBalance starting balance * @param startMonthlyFee starting monthly fee */ public CheckingAccount( double startBalance, double startMonthlyFee ) { super( startBalance ); // call BankAccount constructor setMonthlyFee( startMonthlyFee ); }
/** applyMonthlyFee method charges the monthly fee to * the account */ public void applyMonthlyFee( ) { withdraw( monthlyFee ); } /** accessor method for monthlyFee * @return monthlyFee */ public double getMonthlyFee( ) { return monthlyFee; }
/** mutator method for monthlyFee * @param newMonthlyFee new value for monthlyFee */ public void setMonthlyFee( double newMonthlyFee ) { if ( monthlyFee >= 0.0 ) monthlyFee = newMonthlyFee; else System.err.println( "Monthly fee cannot be negative" ); } }
Example 10.8 public static void main( String [ ] args ) { CheckingAccount c3 = new CheckingAccount( 100.00, 7.50 ); System.out.println( "New checking account: " + c3.toString( ) + "; monthly fee is “ + c3.getMonthlyFee( ) ); c3.applyMonthlyFee( ); // charge the fee to the account System.out.println( "\nAfter charging monthly fee: " + c3.toString( ) + "; monthly fee is “ + c3.getMonthlyFee( ) ); } ----jGRASP exec: java CheckingAccountClientNew checking account: balance is $100.00; monthly fee is 7.5After charging monthly fee: balance is $92.50; monthly fee is 7.5
Software Engineering Tip The superclasses in a class hierarchy should contain fields and methods common to all subclasses. The subclasses should add specialized fields and methods.
Overriding Inherited Methods • A subclass can override (or replace) an inherited method by providing a new version of the method. • The API of the new version must match the inherited method. • When the client calls the method, it will call the overridden version. • The overridden (original) method is invisible to the client of the subclass, but the subclass methods can still call the overridden (original) method using this syntax: super.methodName( argument list )
Overriding • A method in the parent class can be invoked explicitly using the super reference • If a method is declared with the final modifier, it cannot be overridden • The concept of overriding can be applied to data and is called shadowing variables • Shadowing variables should be avoided because it tends to cause unnecessarily confusing code
Shadowing Example public class CheckingAccount extends BankAccount { public final double DEFAULT_FEE = 5.00; private double monthlyFee; private double balance; // this hides the balance from BankAccount /** overloaded constructor calls BankAccount overloaded constructor * @param startBalance starting balance * @param startMonthlyFee starting monthly fee */ public CheckingAccount( double startBalance, double startMonthlyFee ) { setBalance( startBalance ); // call our mutator for initializing setMonthlyFee( startMonthlyFee ); } // whenever balance is referenced it’s the Checking Account balance // BankAccount balance is in the donut hole, but hidden, UNLESS you call a super method that access balance, and then it’s the BankAccount balance // confused?
Overloading vs. Overriding • Overloading deals with multiple methods with the same name in the same class, but with different signatures • Overriding deals with two methods, one in a parent class and one in a child class, that have the same signature • Overloading lets you define a similar operation in different ways for different parameters • Overriding lets you define a similar operation in different ways for different object types
The toString Method • The toString method in the CheckingAccount class overrides the toString method in the BankAccount class. • The subclass version call the superclass version to return balance. public String toString( ) { return super.toString( ) + "; monthly fee is " + MONEY.format( monthlyFee ); }
Example 10.10 public class CheckingAccountClient { public static void main( String [] args ) { CheckingAccount c4 = new CheckingAccount( 100.00, 7.50 ); System.out.println( "New checking account:\n" + c4 ); } } ----jGRASP exec: java CheckingAccountClientNew checking account:balance is $100.00; monthly fee is $7.50 ----jGRASP: operation complete.
Common ErrorTrap • Do not confuse overriding a method with overloading a method. • Overriding a method: • A subclass provides a new version of that method (same signature), which hides the superclass version from the client. • Overloading a method: • A class provides a version of the method, which varies in the number and/or type of parameters (different signature). A client of the class can call any of the public versions of overloaded methods.
protected Members • protected members are accessible by subclasses (like public members), while still being hidden from client classes (like private members). • Also, any class in the same package as the superclass can directly access a protected field, even if that class is not a subclass. • Disadvantage: • Because more than one class can directly access a protected field, protected access compromises encapsulation and complicates maintenance of a program. • For that reason, try to use private, rather than protected, for instance variables.
The protected Access Modifier • Declaring fields as private preserves encapsulation. • Subclass methods call superclass methods to set the values of the fields, and the superclass methods enforce the validation rules for the data. • But calling methods incurs processing overhead. • Declaring fields as protected allows them to be accessed directly by subclass methods. • Classes outside the hierarchy and package must use accessors and mutators for protected fields.
protected fields: Tradeoffs • Advantage: • protected fields can be accessed directly by subclasses, so there is no method-invocation overhead. • Disadvantage: • Maintenance is complicated because the subclass also needs to enforce validation rules. • Recommendation: • Define protected fields only when high performance is necessary. • Avoid directly setting the values of protected fields in the subclass.
Example 10.11 public class BankAccount { public final DecimalFormat MONEY = new DecimalFormat( "$#,##0.00" ); protected double balance; /** Default constructor sets balance to 0.0 */ public BankAccount( ) { balance = 0.0; } /** Overloaded constructor * @param startBalance beginning balance */ public BankAccount( double startBalance ) { deposit( startBalance ); } // rest of the class is the same
Example 10.12 public class CheckingAccount extends BankAccount { public final double DEFAULT_FEE = 5.00; private double monthlyFee; /** default constructor explicitly calls the BankAccount default constructor * set monthlyFee to default value */ public CheckingAccount( ) { super( ); // call BankAccount constructor monthlyFee = DEFAULT_FEE; } /** overloaded constructor calls BankAccount overloaded constructor * @param startBalance starting balance * @param startMonthlyFee starting monthly fee */ public CheckingAccount( double startBalance, double startMonthlyFee ) { super( startBalance ); // call BankAccount constructor setMonthlyFee( startMonthlyFee ); }
/** applyMonthlyFee method charges the monthly fee to the account */ public void applyMonthlyFee( ) { balance -= monthlyFee; if ( balance < 0.0 ) System.err.println( "Warning: account is overdrawn" ); } /** accessor method for monthlyFee @return monthlyFee */ public double getMonthlyFee( ) { return monthlyFee; } /** mutator method for monthlyFee * @param newMonthlyFee new value for monthlyFee */ public void setMonthlyFee( double newMonthlyFee ) { if ( monthlyFee >= 0.0 ) monthlyFee = newMonthlyFee; else System.err.println( "Monthly fee cannot be negative" ); }
/* toString method * @return String containing formatted balance and monthlyFee * invokes superclass toString to format balance */ public String toString( ) { return super.toString( ) + "; monthly fee is " + MONEY.format( monthlyFee ); } }