8. Inheritance and
Interfaces
To learn about
inheritances
To be able to convert
between supertype
and subtype
references
To understand how to
override superclass
methods
To understand the
concept of
polymorphism
To design and use
abstract classes and
interfaces
To learn about
packages and Java
access control
Introduction to
Inheritance
Inheritance is a
mechanism for
enhancing existing,
working class.
For example, suppose
we need to define a
class SavingsAccount
to model an account
that pays a fixed
interest rate on
deposits.
class SavingsAccount
extends BankAccount
{ new instance variables
new methods
}
class SubclassName
extends SuperclassName
{ new instance variables
new methods
}
Inheritance
The more general
class that forms the
basis for inheritance is
called the superclass.
The more specialized
class that inherits from
the superclass is
called the subclass.
One important reason
for inhertance is code
reuse.
Inheritance Diagram
Object <---
BankAccount <----
SavingsAccount
Inheritance Diagram
Which class is a
superclass and which
class is a subclass?
BankAccount is the
superclass and
SavingsAccount is the
subclass.
Suppose we want to
set an interest rate in
the constructor for
SavingsAccount class
and also a method to
apply that interest rate
periodically.
public class
SavingsAccount extends
BankAccount
{ private double
interestRate;
public SavingsAccont
(double rate)
{ constructor
implementation }
public void addInterest
( )
{ method
implementation }
}
public class
BankAccount
{ private double
balance;
public BankAccount( )
{ balance = 0; }
public BankAccount
(double init )
{ balance = init; }
public void deposit
(double amount)
{ balance = balance +
amount; }
public void withdraw
(double amount)
{ balance = balance -
amount; }
public double
getBalance( )
{ return balance; }
}
public class
SavingsAccount
extends BankAccount
{ private double
interestRate; //
instance variable
public SavingsAccont
(double rate) //
constructor
{ interestRate = rate; }
public void addInterest
( ) // instance method
{ double interest =
getBalance() *
interestRate / 100;
deposit(interest);
}
}
// Example Code
SavingsAccount
collegeFund = new
SavingsAccount(10);
collegeFund.deposit
(500); // OK to use
superclass’s method
collegeFund.
addInterest( );
Converting Between
Class Types
A SavingsAccount
object is a special
case of a BankAccount
object. So, you can
store a reference to a
SavingsAccount object
into a BankAccount
object.
SavingsAccount
collegeFund = new
SavingsAccount(10);
BankAccount anAccount =
collegeFund;
Object anObject =
collegeFund;
The three object
references stored in
collegeFund,
anAccount, and
anObject refer to the
same object of type
SavingsAccount.
However, the
anAccount knows less
than the full story
about the object to
which it refers
anAccount.deposit(1000)
; // OK
anAccount.addInterest( )
; // Not OK
Because anAccount is
an object of type
BankAccount, we
cannot use the
addInterest method.
anObject.deposit(1000); //
Not OK
The variable anObject
knows even less,
because it is an object
of type Object and
deposit is not a
method of the object
class.
Why would anyone
want to know less
about an object and
store a reference in an
object variable of a
superclass? Reuse.
Consider the transfer
method
void transfer(BankAccount
other, double amount)
{ withdraw(amount);
other.deposit(amount);
}
We can use this
method to transfer
money from one bank
account to another:
BankAccount
momsChecking = … ;
BankAccount
harrysChecking = … ;
momsChecking.transfer
(harrysChecking, 1000);
We can also use this
method to transfer
money into a
SavingAccount:
SavingsAccount
CollegeFund = … ;
momsChecking.
transfer(collegeFund,
1000);
However, we cannot
convert between
unrelated classes:
Rectangle r =
CollegeFund; // Error
Subclass reference can
be assigned to
superclass reference
Object anObject =
collegeFund;
But, superclass
reference must be
casted to subclass
reference.
SavingsAccount x =
(SavingsAcount)anObject;
as long as we
absolutely sure that
anObject really refers
to a SavingsAccount
object.
To play it safe, we can
use the instanceof
operator to test
whether an object
belongs to a particular
class (or one of its
subclasses).
Object instanceof
ClasssName
Rectangle r;
if (x instanceof
Rectangle)
r = (Rectangle) x;
SavingsAccount x;
if (anObject instanceof
SavingsAccount)
x = (SavingsAccount)
anObject;
else
x = null;
Hierarchies
Hierarchies are
frequently represented
as trees, with the
most general concepts
at the root.
In Java, it is common
to group classes in
inheritance
hierarchies.
Inheritance
Hierarchies
The classes
representing the most
general concepts are
near the root, more
specialized classes
towards the branches.
When designing a
hierarchy of classes,
those common
properties are
collected in a
superclass. More
specialized properties
can be found in
subclasses.
Consider a bank that
offers its customers
three kinds of
accounts:
CheckingAccount
SavingsAccount
TimeDepositAccount
The checking account
has no interest, gives
you a small number of
free transactions per
month, and charges a
transaction fee for
each additional
transactions.
The savings account
compounds interest
monthly.
The time deposit
account has a penalty
for early withdrawal.
The inheritance
hierarchy for these
account classes is:
protected Members
A superclass’s public
members are
accessible anywhere
the program has a
reference to that
superclass type or one
of its subclasses
types.
A superclass’s private
members are
accessible only in
methods of that
superclasses.
A superclass’s
protected members
serve as an
intermediate level of
protection between
public and private
access.
protected Members
A superclass’s
protected members
can be accessed by
methods of the
superclass, by
methods of subclasses
and by methods of
other classes in the
same package
(protected members
have package access).
Subclass methods can
normally refer to public
and protected
members of the
superclass simply by
using the member
names.
Inheritance
// Definition of a class
Point
public class Point {
protected int x, y;
public Point()
{ setPoint(0,0); }
public Point(int a, int b)
{ setPoint(a, b); }
public void setPoint(int
a, int b)
{ x = a;
y = b;
}
public int getX()
{ return x; }
public int getY()
{ return y; }
public String toString()
{ return “[“ + x + “, “ + y
+ “]”; } }
// Definition of class
circle
public class Circle
extends Point {
protected double
radius;
public Circle()
{ // implicit call to
superclass constructor
setRadius(0); }
public Circle(double r,
int a, int b)
{ super(a, b);
setRadius(r);
}
public void setRadius
(double r)
{ radius = (r >= 0.0 ? r :
0.0); }
pubic double getRadius
()
{ return radius; }
public double area()
{ return Math.PI *
radius * radius; }
public String toString()
{ return “Center = “ +
“[“ + x + “,” + y + “]” +
“; Radius = “ + radius;
}
}
// Main program
import java.text.
DecimalFormat;
import javax.swing.
JOptionPane;
public class
InheritanceTest {
public static void main
(String[] args)
{ Point pointRef, p;
Circle circleRef, c;
String output;
p = new Point(30,50);
c = new Circle(2.7, 120,
89);
output = “Point p: “+
p.toString() +
“\nCircle c: “ +
c.toString() ;
pointRef = c;
output += “\n\nCircle c
(via pointRef): “ +
pointRef.toString() ;
circleRef = (Circle)
pointRef;
output += “\n\nCircle c
(via circleRef): “ +
circleRef.toString() ;
DecimalFormat
precision2 = new
DecimalFormat(“0.00”)
;
output += “\n\nArea of
c (via circleRef): “ +
precision2.format
(circleRef.area());
if (p instanceof Circle)
{
circleRef = (Circle) p;
output += “\n\ncast
successful”;
}
else
output += “\n\n p does
not refer to a circle”;
JOptionPane.
showMessageDialog
(null, output,
“Inheritance”,
JOptionPane.
INFORMATION_
MESSAGE);
System.exit(0);
}
}
Inheriting Instance
Variables and Methods
When defining
additional methods for
a subclass, there are 3
options:
1. We can override
methods from the
superclass by
specifying a method
with the same
signature (i.e., the
same name and the
same parameters).
2. We can inherit
methods from the
superclass. If we do
not explicitly override
a superclass method,
we automatically
inherit it.
3. We can define new
methods that does not
exist in the superclass.
When defining
additional instance
variables for a
subclass, there are 2
options:
1. We can inherit
variables from the
superclass.
2. We can define new
variables. These new
variables are only
presented in subclass
objects.
Remember! We can
never override
instance variables.
Shadowing
The newly defined
subclass variable
shadows the
superclass variable.
The superclass
variable is still
present, but it cannot
be accessed.
When you refer to
balance in a
SavingAccount
method, you access
the new instance
variable.
Shadowing instance
variables is harmless.
Shadowing Instance
Variables
A subclass has no
access to the private
instance of the
superclass
public class
CheckingAccount
extends BankAccount
{ public void deposit
(double amount)
{ transactionCount++;
balance = balance +
amount; // ERROR
}
}
To solve the problem
public class
CheckingAccount
extends BankAccount
{ private double
balance; // shadow
variable
public void deposit
(double amount)
{ transactionCount++;
balance = balance +
amount; // OK but
different balance
}
}
Example
public class
BankAccount
{ private double
balance;
public double
getBalance() {…}
public void deposit
(double amount) {…}
public void withdraw
(double amount) {…}
}
public class
CheckingAccount
extends BankAccount
{ private int
transactionCount;
public void deposit
(double amount) {…}
public void withdraw
(double amount) {…}
public void deductFees
() {…}
}
Subclass methods
have no access rights
to the private data of
the superclass.
If we want to modify a
private superclass
variable, we must use
a public method of the
superclass.
Can we write the
deposit method as
public void deposit
(double amount)
{ transactionCount++;
deposit(amount); // Is
it OK?
}
If we define the
method like that, it will
call itself infinitely.
We need to be more
specific that we want
to invoke the
superclass’s deposit
method by using a
special keyword super
for this purpose:
public void deposit
(double amount)
{ transactionCount++;
super.deposit(amount);
}
Calling a Superclass
Methods
public class
CheckingAccount
extends BankAccount
{ private static final int
FREE_TRANSANCTIONS
= 3;
private static final
double TRANSACTION_
FEE = 2.0;
public void withdraw
(double amount) //
override method
{ transactionCount++;
super.withdraw
(amount); // calling a
superclass method.
// what if we call
withdraw(amount);
}
public void deductFees
( )
{ if (transactionCount >
FREE_TRANSACTIONS)
{ double fees =
TRANSACTION_FEE
* (transactionCount -
FREE_TRANSACTIONS);
super.withdraw(fees);
}
}
Now, let consider the
TimeDepositAccount
class, a subclass of the
SavingsAccount:
public class
TimeDepositAccount
extends
SavingsAccount
{ private int
periodsToMaturity;
private static final
double EARLY_
PENALTY= 20.0;
public void addInterest
( )
{ periodsToMaturity- - ;
super.addInterest( );
}
public void withdraw
(double amount)
{ if (periodsToMaturity
> 0)
super.withdraw(EARLY_
PENALTY);
super.withdraw
(amount);
}
}
The
TimeDepositAccount
is two levels away
from the BankAccount.
The BankAccount is a
superclass, but not the
immediate superclass.
The
TimeDepositAccount
inherits two methods
from the BankAccount
class, namely
getBalance, and
deposit.
Methods can be
inherited from an
indirect superclass as
long as none of the
intermediate
superclasses
overrides them.
The method call
super.addInterest in
addInterest method of
the
TimeDepositAccount
refers to the
addInterest method in
SavingsAccount class.
The super.withdraw in
the withdraw method
of the
TimeDepositAccount
refers to the withdraw
method in
BankAccount.
Subclass Construction
Calling a Superclass
Constructor
ClassName
(parameters)
{ super(parameters);
…
}
Call the superclass
constructor to set the
balance to the initial
balance
public class
CheckingAccount
extends BankAccount
{ public
CheckingAccount
(double init)
{ super(init);
transactionCount = 0;
}
}
The constructor call
must be the first
statement of the
subclass constructor.
Subclass Constructor
When the keyword
super is followed by a
parenthesis, it
indicates a call to the
superclass
constructor.
If super is followed by
a period and a method
name, it indicates a
call to a superclass
method.
Or, we can implement
the CheckingAccount
constructor without
calling the superclass
constructor as can be
seen in the following
example:
public class
CheckingAccount
extends BankAccount
{ public
CheckingAccount
(double init)
{ // use superclass’s
default constructor
super.deposit(init);
transactionCount = 0;
}
}
public class
TimeDepositAccount
extends
SavingsAccount
{ public
TimeDepositAccount
(double rate, int
maturity )
{ super(rate);
periodsToMaturity =
maturity ;
}
}
Subclass Construction
If a subclass
constructor does not
call the superclass
constructor, the
superclass is
constructed with its
default constructor.
If a superclass does
not have a default
constructor, then the
complier reports an
error.
For example,
CheckingAccount can
be implemented
without calling the
superclass
constructor.
The BankAccount class
is constructed with its
default constructor,
which set the balance
to zero.
Then the
CheckingAccount
constructor must
explicitly deposit the
initial balance.
public
CheckingAccount
(double init)
{ // use superclass’s
default constructor
super.deposit(init);
transactionCount = 0;
However, in the case
of
TimeDepositAccount,
the superclass,
SavingsAccount, has
no default constructor.
Thus, we must call the
superclass constructor
explicitly.
public
TimeDepositAccount
(double rate, int
maturity )
{ super(rate);
periodsToMaturity =
maturity ;
}
Polymorphism
“is-a” relationship:
Every object of the
subclass is also a
superclass object, but
with special
properties.
For example, every
CheckingAccount is a
BankAccount.
It is possible to use a
subclass object in
place of a superclass
object.
What is polymorphism?
Let’s consider the
transfer method that
can be used to transfer
money from another
account:
public void transfer
(BankAccount other,
double amount)
{ withdraw(amount);
other.deposit(amount);
}
BankAccount
collegeFund = … ;
CheckingAccount
harrysChecking = … ;
collegeFund.transfer
(harrysChecking, 1000)
;
Which deposit
method?
BankAccount.deposit( )
or CheckingAccount.
deposit( )
Consider the call to the
deposit method, the
other parameter has
type BankAccount.
On the other hand, the
CheckingAccount class
provides its own
deposit method that
updates the
transaction count.
Since the other
variable actually refers
to an object of the
subclass
CheckingAccount, it
would be appropriate if
the CheckingAccount.
deposit method was
called instead.
The principle that the
actual type of the
object determines the
method to be called is
called polymorphism.
In Java, method calls
are always determined
by the type of the
actual object, not the
type of the object
reference.
In Java, all instance
methods are
polymophic.
The term
“polymorphism”
comes from the Greek
words for “many
shapes”.
Program
AccountTest.java
public class AccountTest
{ public static void main
(String[] args)
{ SavingsAccount
momsSavings = new
SavingsAccount(0.5);
TimeDepositAccount
collegeFund =
new TimeDepositAccount
(1, 3);
CheckingAccount
harrysChekcing = new
CheckingAccount(0);
momsSavings.deposit
(10000);
collegeFund.deposit
(10000);
momsSavings.transfer
(harryschecking, 2000);
collegeFund.transfer
(harryschecking, 980);
harrysChecking.withdraw
(500);
harrysChecking.withdraw
(80);
harrysChecking.withdraw
(400);
endOfMonth
(momsSavings);
endOfMonth
(collegeFund);
endOfMonth
(harrysChecking);
printBalance(“mom’s
savings”,
momsSavings);
printBalance(“the
college Fund”,
collegeFund);
printBalance(“Harry’s
checking”,
harrysChecking);
}
public static void
endOfMonth
(SavingsAccount savings)
{ savings.addInterest
( ); }
public static void
endOfMonth
(CheckingAccount
checking)
{ checking.deductFees
( ); }
public static void
printBalance(String
name, BankAccount
account)
{ System.out.println
(“The balance of “ +
name + “ account is $”
+
account.getBalance( ) )
;
}
}
Polymorphism vs.
Overload
Let’s take a look at a
polymorphic method
call such as
other.deposit(amount),
there are several
deposit methods that
can be called.
Both polymorphic
deposit and withdraw
methods are called.
What happens when
the method name is
overloaded;i.e., when
a single class has
several methods with
the same name but
different parameter
types?
For example, we can
have two constructors
BankAccount() and
BankAccount(double).
The compiler selects
the appropriate
method when
compiling a program.
Another example of
overloading, the static
endOfMonth methods
in the AccountTest
class. The compiler
uses the type of the
explicit parameter to
pick the appropriate
method.
The main difference
between
polymorphism and
overloading is the
binding method.
Polymorphism uses a
selection method
called late binding, i.e.,
the compiler doesn’t
make any decision
when translating the
method, it is the virtual
machine that selects
the appropriate
method.
Overloading uses early
binding technique
which compiler picks
an overloaded method
when translating the
program.
Interfaces
Many object-oriented
programming
languages have
multiple inheritance,
i.e.,more than one
superclass.
In Java, a class cannot
have two direct
superclasses.
To support this
feature, we use
interfaces instead of
superclasses.
The purpose of
interfaces is to support
the concept of writing
a single method for
any data type.
Interfaces
An interface is similar
to a class, but
An interface does not
have instance
variables
All methods in an
interface are abstract;
they have a name,
parameters, and a
return type, but they
don’t have an
implementation.
All methods in an
interface are
automatically public.
The interface
declaration lists all
methods that the
interface requires.
For example, the
java.lang package
defines a Comparable
interface as:
public interface
Comparable
{ int compareTo(Object
other);
// no implementation
}
We don’t extend
interfaces; we
implement them, using
the special
implements keyword:
public class
SavingAccount extends
BankAccount
implements
Comparable
{ …
}
Any class that
implements the
Comparable interface
must supply the
compareTo method
public class
SavingsAccount
extends BankAccount
implements
Comparable
{ …
public int compareTo
(Object other)
{ // supply
implementation …
SavingsAccount
otherAcc =
(SavingsAccount)
other;
if (interestRate <
otherAcc.interestRate)
return -1;
if (interestRate >
otherAcc.interestRate)
return 1;
return 0;
} ...
}
Note that the class
must declare the
method as public,
whereas the interface
does not -- all methods
in an interface are
public.
Once a class
implements an
interface, you can
convert a class
reference to an
interface reference:
SavingsAccount
harrysSavings = new
SavingAccount();
Comparable first =
harrysSavings;
However, we can
never construct an
interface:
Comparable second; // OK
second = new
Comparable( ); // ERROR
All interface
references refer to
objects of other
classes-- classes that
implement the
interface.
first.compareTo(second);
A class can have only
one superclass, but it
can implement any
number of interfaces
public class
SavingsAccount
extends BankAccount
implements
Comparable, Cloneable
Constants in
Interfaces
public interface
SwingConstants
{ int NORTH = 1;
int NORTH_EAST = 2;
int EAST = 3;
…
}
// SwingConstants.
NORTH;
all variables in an
interface are
automatically public
static final
Abstract Classes
Abstract Classes : A
class which we cannot
create objects
public abstract class
BankAccount
{ publicabstract void
deductFees( );
…
}
The reason for using
abstract classes is to
force programmers to
create subclasses.
BankAccount
anAccount; // OK
anAccount = new
BankAccount(); // Error
anAccount = new
SavingsAccount(); //
OK
anAccount = null; // OK
Note that we can still
have an object
reference whose type
is an abstract class.
Abstarct classes differ
from interfaces -- they
can have instance
variables and concrete
methods.
Final Methods and
Classes: To prevent
others from creating
subclasses or from
overriding certain
methods
public final class String
{ …. }
public class MyApplet
extends Applet
{ publicfinal boolean
checkPasswd(String
passwd)
{ … }
}
Access Control
Java has four levels of
controlling access to
variables, methods,
and classes:
public access
private access
protected access
Package access (the
default, when no
access modifier is
given)
Proteced data can be
accessed by the
methods of a class and
all its subclasses.
public class
BankAccount
{ protected double
balance; }
If a superclass
declares a method to
be publicly accesible,
we cannot override it
to be more private.
public class BankAccount
{ public void withdraw
(double amount) { … } }
public class
TimeDepositAccount
extends BankAccount
{ private void withdraw
(double amount) { … } //
ERROR
}
Overriding Object
Superclass Methods
The methods of the
Object class are very
general:
- String toString( ) //
Returns a string
representation of the
object
- boolean equals
(Object other) // Test
whether the object
equals
// another object
- Object clone( ) //
Makes a full copy of an
object
Overriding the toString
Method
The toString method
returns a string
representation for
each object.
Rectangle x = new
Rectangle(5, 10, 20, 30);
String s = x.toString( ); //
set s to
“java.awt.Rectangle[x=5,
// y=10,
width=20,height=30]”
The toString method is
called whenever we
concatenate a string
with an object.
System.out.println(“x
means ” + x); // will print
“x means
// java.awt.Rectangle
[x=5,y=10,width=20,height
=30]”
Let’s try the toString
method for the
BankAccount class:
BankAccount x = new
BankAccount(5000);
String s = x.toString( ); //
sets s to “BankAccount@
d246bf”
// print the name of the
class and the memory
address of the object
To work better,
overriding toString
method:
public class BankAccount
{ public String toString()
{ return “BankAccount
[balance=” + balance + “]
”; }
}
BankAccount x = new
BankAccount(5000);
String s = x.toString( );
// sets s to
“BankAccount
[balance=5000]”
Overriding the equals
Method
The equals method is
called whenever we
want to compare two
objects:
if (account1.equals
(account2)) …
// contents are the same
Compare to:
if (account1 = = account2)
…
// test two references are
the same object
public class
BankAccount
{ public boolean equals
(Object otherObj)
{ if (otherObj
instanceof
BankAccount)
{ BankAccount other =
(BankAccount)
otherObj;
return balance = =
other.balance;
}
else
return false;
}
}
Overriding the clone
method
To make a copy of an
object:
public class BankAccount
{ public Object clone( )
{ BankAccount clonedAcc
= new BankAccount
(balance);
return clonedAcc;
}
}
When we call the
method:
BankAccount acc1 = new
BankAccount(1000);
BankAccount acc2 =
(BankAccount)acc1.clone();
Compared to:
BankAccount acc1 =
new BankAccount
(1000);
BankAccount acc2 =
acc1;
Clone Mutable
Instance Variables
Consider the following
class:
public class Customer
{ private String name;
private BankAccount
account;
public Customer(String
aName)
{ name = aName;
account = new
BankAccount();
}
public String getName()
{ return name; }
public BankAccount
getAccount()
{ return account; }
getAccount method
breaks encapsulation
because anyone can
modify the object
state without going
through the public
interface:
Customer harry = new
Customer(“Harry
Handsome”);
BankAccount account =
harry.getAccount();
// anyone can
withdraw money!
account.withdraw
(100000);
So, we should clone
the object reference:
public BankAccount
getAccount()
{ return (BankAccount)
account.clone(); }
The rule of thumb is
that a class should
clone all references to
mutable objects that it
gives out.
The converse is true
as well-- a class
should clone
references that it
receives:
public Customer(String
aName, BankAccount
anAccount)
{ name = aName;
account = (BankAccount)
anAccount.clone();
}
Using Object.clone()
If we have an object
with many instance
variables, it can be
tedious to copy them
all into a new object.
The Object.clone()
simply creates a new
object whose instance
variables are copies of
the original.
public class
BankAccount
{ public Object clone()
{ // not complete
Object clonedAccount
= super.clone();
return clonedAccount;
}
}
Object.clone() checks
that the object being
cloned implements the
Cloneable interface
and do exception
handling.
public class BankAccount
implements Cloneable
{ public Object clone()
{try
{ // clones all instance
variables
Object clonedAccount =
super.clone();
return clonedAccount;
}
catch (
CloneNotSupportedEx
ception e)
{ return null; }
}
}
If an object contains a
reference to another
mutable object, then
we need to call clone
for that reference, in
order to prevent
shallow copy problem
(see Figure 11).
public class Customer
implements Cloneable
{ private String name;
private BankAcount
account;
public Object clone()
{ try
{ Object
clonedCustomer =
(Customer)super.clone
();
clonedCustomer.
account =
(BankAccount)
account.clone();
return
clonedCustomer;
}
catch (
CloneNotSupportedEx
ception e)
{ return null; }
}
}
Packages
A Java package is a
set of related classes.
Table1: Important
Packages in the Java
Library
java.lang Langauge
supoort Math
java.util Utilites
Random
java.io Input/Output
PrintStream
java.awt Abstract
Windowing Color
Toolkit
java.applet Applets
Applet
java.net Networking
Socket
java.sql Database
access ResultSet
javax.swing Swing UI
JButton
To put classes in a
package:
package
packagename;
package
com.horstmann.ccj;
import
java.io.InputStream;
import java.io.
InputStreamReader;
public class
ConsoleReader { … }
….
Importing Packages
import
com.horstmann.ccj.
ConsoleReader;
Friday, 1 July 2011
Subscribe to:
Post Comments (Atom)
0 comments:
Post a Comment