Learning Objects for Inheritance
Concept Inheritance is an important technique for structuring object-oriented programs. Given a class (called a superclass) it can be extended to a subclass. The subclass inherits all the fields of the superclass and it can add additional fields. The subclass inherits methods of the superclass and it can add new methods or override the inherited methods with its own versions.
These source code of these learning objects can be found in inheritance.zip.
LO | Topic | Java Files (.java) | Prerequisites |
"Inheriting fields" | Inheriting fields | Inheritance01 | |
"Inheriting methods" | Inheriting and overriding methods | Inheritance02 | 1 |
"Dynamic dispatching" | Dynamic dispatching | Inheritance03 | 2 |
"Downcasting" | Downcasting | Inheritance04 | 3 |
"Heterogeneous data structures" | Heterogeneous data structures | Inheritance05 | 4 |
"Abstract classes" | Abstract classes | Inheritance06 | 5 |
"Equals" | Equals | Inheritance07A, B, C | 2 |
"Clone" | Clone | Inheritance08 | 2 |
"Overloading vs. overriding" | Overloading vs. overriding | Inheritance09 | 3 |
Program The running example is a framework for the simulation of moving particles. There is a class
Particle
with a field position
that is updated by the method newPosition
. There are three subclasses:AParticle
is derived directly fromParticle
and adds the fieldspin
. The methodnewPosition
is overridden.BParticle
is derived directly fromParticle
and adds the fieldcharge
. The methodnewPosition
is not overridden.CParticle
is derived directly fromBParticle
and thus indirectly fromAParticle
. It adds the fieldstrange
; the methodnewPosition
is overridden.
For each program the following initialization is performed and will not be explicitly mentioned for each learning object; instead, the step “the objects are created” will be listed:
- Variable are declared and assigned the null value.
- Memory is allocated for each object's fields and are given default values. For a subclass, these fields include all fields of its superclasses.
- In the constructors, a subclass calls
super
to initialize the fields declared by the superclasses and then initializes its own fields.
Tip: Use
Animation / Run Until ...
to skip over the animation of the initialization.Tip: Several of the LOs will ask you to check that a certain version of a method is called. This can be done by looking at the source code in the left panel: the method called is highlighted in blue.
Inheriting fields
Concept Subclasses inherit all the fields of its superclasses; they can also add fields of their own.
Program: Inheritance01.java
// Learning Object Inheritance01
// inheriting fields
class Particle {
int position;
Particle(int p) {
position = p;
}
void newPosition(int delta) {
position = position + delta;
}
}
class AParticle extends Particle {
double spin;
AParticle(int p, double s) {
super(p);
spin = s;
}
void newPosition(int delta) {
if (spin < delta)
position = position + delta;
}
}
class BParticle extends Particle {
int charge;
BParticle(int p, int c) {
super(p);
charge = c;
}
}
class CParticle extends BParticle {
boolean strange;
CParticle(int p, int c, boolean s) {
super(p, c);
strange = s;
}
void newPosition(int delta) {
if (strange)
position = position * charge;
}
}
class Inheritance01 {
public static void main(/*String[] args*/) {
Particle p = new Particle(10);
AParticle a = new AParticle(20, 2.0);
BParticle b = new BParticle(30, 3);
CParticle c = new CParticle(40, 4, true);
int pPosition = p.position;
int aPosition = a.position;
double aSpin = a.spin;
int bPosition = b.position;
int bCharge = b.charge;
int cPosition = c.position;
int cCharge = c.charge;
boolean cStrange = c.strange;
}
}
In this simple program objects of all four classes are created and their fields are read.
- The objects are created.
- For each field of each object, a variable is declared in the main method and the value of the field is assigned to it. Check that each value originates from the correct field.
Exercise In
CParticle
, add the declaration int charge = -1;
. Compile and run the program. Is the output different? Explain what happens.Inheriting methods
Concept Subclasses inherit the methods of its superclasses and can add new methods of its own. You can override an inherited method by writing a new method with the same signature as the inherited method.
Program: Inheritance02.java
// Learning Object Inheritance02
// inheriting and overriding methods
class Particle {
int position;
Particle(int p) {
position = p;
}
void newPosition(int delta) {
position = position + delta;
}
}
class AParticle extends Particle {
double spin;
AParticle(int p, double s) {
super(p);
spin = s;
}
void newPosition(int delta) {
if (spin < delta)
position = position + delta;
}
}
class BParticle extends Particle {
int charge;
BParticle(int p, int c) {
super(p);
charge = c;
}
}
class CParticle extends BParticle {
boolean strange;
CParticle(int p, int c, boolean s) {
super(p, c);
strange = s;
}
void newPosition(int delta) {
if (strange)
position = position * charge;
}
}
class Inheritance02 {
public static void main(/*String[] args*/) {
Particle p = new Particle(10);
AParticle a = new AParticle(10, 2.0);
BParticle b = new BParticle(10, 3);
CParticle c = new CParticle(10, 4, true);
p.newPosition(10);
int pPosition = p.position;
a.newPosition(10);
int aPosition = a.position;
b.newPosition(10);
int bPosition = b.position;
c.newPosition(10);
int cPosition = c.position;
}
}
This program calls the method
newPosition
, which is overridden in AParticle
and CParticle
but not in BParticle
.- The objects are created.
- Method
newPosition
is invoked for each object and the modified value ofposition
is assigned to a variable. - Check that the call on
p
calls the method defined in classParticle
. - Check that the call on
a
calls the method defined in the classAParticle
; this method overrides the method declared in classParticle
. - Check that the call on
b
calls the method defined in the superclassParticle
; since the method was not overridden inBParticle
, the method called is the one inherited from the superclass. - Check that the call on
c
calls the method defined in the classBParticle
; this method overrides the method declared in classParticle
.
Exercise Remove the method
newPosition
from CParticle
. Which method is invoked for c.newPosition
?Exercise Remove the method
newPosition
from CParticle
and add a method with the same signature to BParticle
. Which method is invoked for c.newPosition
?Dynamic dispatching
Concept A variable
v
of type T
can contain a reference to an object of type T
or of the type of any subclass of T
. When invoking v.m
for some method m
that is overridden in a subclass, it is the type of the object currently referenced by v
(not the type of the variablev
) that determines which method is called. This is called dynamic dispatching because the call is dispatched at runtime.Program: Inheritance03.java
// Learning Object Inheritance03
// dynamic dispatching
class Particle {
int position;
Particle(int p) {
position = p;
}
void newPosition(int delta) {
position = position + delta;
}
}
class AParticle extends Particle {
double spin;
AParticle(int p, double s) {
super(p);
spin = s;
}
void newPosition(int delta) {
if (spin < delta)
position = position + delta;
}
}
class BParticle extends Particle {
int charge;
BParticle(int p, int c) {
super(p);
charge = c;
}
}
class CParticle extends BParticle {
boolean strange;
CParticle(int p, int c, boolean s) {
super(p, c);
strange = s;
}
void newPosition(int delta) {
if (strange)
position = position * charge;
}
}
class Inheritance03 {
public static void main(/*String[] args*/) {
Particle p = new Particle(10);
AParticle a = new AParticle(20, 2.0);
BParticle b = new BParticle(30, 3);
CParticle c = new CParticle(40, 4, true);
p.newPosition(10);
int pPosition = p.position;
p = a;
p.newPosition(10);
int aPosition = p.position;
p = b;
p.newPosition(10);
int bPosition = p.position;
p = c;
p.newPosition(10);
int cPosition = p.position;
}
}
- The objects are created.
- The references to the objects are assigned one-by-one to the variable
p
, and then the methodnewPosition
is invoked usingp
. The modified value ofposition
is assigned to a variable. - Check that the call on
p
invokes the method defined in classParticle
. - After assigning
a
top
, check that the call invokes the method defined in the classAParticle
; this method overrides the method declared in classParticle
. Although the type ofp
is classParticle
, it holds a reference to an object whose type is classAParticle
so the method of that class is called. - Note that as a result of the assignment, the object of type
Particle
has become garbage. - After assigning
b
top
, check that the call invokes the method defined in the superclassParticle
; since the method was not overridden inBParticle
, the method called is the one inherited from the superclass. - After assigning
c
top
, check that the call invokes the method defined in the classCParticle
; this method overrides the method declared in classParticle
. Although the type ofp
is classParticle
, it holds a reference to an object whose type is classCParticle
so the method of that class is called.
Exercise Add an assignment of
c
to b
and call b.newPosition(10)
. What is the value now of b.position
?Downcasting
Concept A variable of the type of a class can reference an object of the type of a subclass, but this variable cannot be used to access fields declared in the subclass. Nevertheless, the object “remembers” its type, even if it is assigned to a variable of the type of its superclass, and the type can be “recovered” by casting to a variable of the type of the subclass. This is called downcasting because the cast is “down” the derivation hierarchy.
Program: Inheritance04.java
// Learning Object Inheritance04
// downcasting
class Particle {
int position;
Particle(int p) {
position = p;
}
void newPosition(int delta) {
position = position + delta;
}
}
class AParticle extends Particle {
double spin;
AParticle(int p, double s) {
super(p);
spin = s;
}
void newPosition(int delta) {
if (spin < delta)
position = position + delta;
}
}
class BParticle extends Particle {
int charge;
BParticle(int p, int c) {
super(p);
charge = c;
}
}
class CParticle extends BParticle {
boolean strange;
CParticle(int p, int c, boolean s) {
super(p, c);
strange = s;
}
void newPosition(int delta) {
if (strange)
position = position * charge;
}
}
class Inheritance04 {
public static void main(/*String[] args*/) {
Particle p = new Particle(10);
AParticle a = new AParticle(20, 2.0);
int pPosition = p.position;
p = a;
int paPosition = p.position;
a = (AParticle) p;
int aPosition = a.position;
double sSpin = a.spin;
}
}
In this program, we take an object of the type of the subclass
AParticle
and assign its reference to the variable p
of the type of the superclass Particle
. The object's actual type is recovered by downcasting from p
to a
.- The objects are created.
- The value of
p.position
is stored in a variable. - The reference in the variable
a
is assigned top
. Note that the arrows from the representation of both variables point to the same object of typeAParticle
, and that the other object is garbage. - When the value of
p.position
is accessed, it refers to the value that is ina.position
. - The reference in
p
can be cast to the typeAParticle
and assigned toa
. Althoughp
is declared to hold references to objects of typeParticle
, the object was really of the subclassAParticle
. - Both the fields
position
andspin
can be accessed througha
.
Exercise What happens if you try to access
p.spin
after a
has been assigned to p
?Exercise Add the statement
BParticle b = (BParticle) p
after the assignment of a
to p
. Does the program compile successfully? Does it run successfully? Explain the results.Heterogeneous data structures
Concept A heterogeneous data structure is one that can hold elements of different types. A data structure whose elements are of the type of a class can hold references to objects of any subclass of that class.
Program: Inheritance05.java
// Learning Object Inheritance05
// heterogeneous data structures
class Particle {
int position;
Particle(int p) {
position = p;
}
void newPosition(int delta) {
position = position + delta;
}
}
class AParticle extends Particle {
double spin;
AParticle(int p, double s) {
super(p);
spin = s;
}
void newPosition(int delta) {
if (spin < delta)
position = position + delta;
}
}
class BParticle extends Particle {
int charge;
BParticle(int p, int c) {
super(p);
charge = c;
}
}
class CParticle extends BParticle {
boolean strange;
CParticle(int p, int c, boolean s) {
super(p, c);
strange = s;
}
void newPosition(int delta) {
if (strange)
position = position * charge;
}
}
class Inheritance05 {
public static void main(/*String[] args*/) {
Particle[] p = new Particle[4];
p[0] = new Particle(10);
p[1] = new AParticle(20, 2.0);
p[2] = new BParticle(30, 3);
p[3] = new CParticle(40, 4, true);
int i = 0;
p[i++].newPosition(10);
p[i++].newPosition(10);
p[i++].newPosition(10);
p[i].newPosition(10);
}
}
An array whose elements are of class
Particle
can store references to objects of any of its subclasses.- The objects are created and references to them assigned to elements of the elements of the array
p
. - Method
newPosition
is invoked for the object referenced by each element of the arrayp
. Check that the fields accessed are those of the object referenced by the array element and that the calls are dynamically dispatched to the method appropriate for the type of the object.
Exercise Every object in Java is a subclass of the class
Object
. Modify the program so that the variable p
is of type array of Object
.Abstract classes
Concept Very often the “root” of a set of derived types has no meaning itself, in the sense that objects of that type would never be declared. For example, in a realistic simulation program, the would be no real particles that are just “particles,” only particles with names like α-particles and β-particles. An abstract class can be declared which serves only as a root from which to derive a hierarchy of subclasses. It is not legal to declare objects of an abstract class, although variables of its type may be declared and used to reference objects of any type within the hierarchy. A method may also be declared abstract; this indicates that it must be overridden in subclasses.
Program: Inheritance06.java
// Learning Object Inheritance06
// abstract classes
abstract class Particle {
int position;
Particle(int p) {
position = p;
}
abstract void newPosition(int delta);
}
class AParticle extends Particle {
double spin;
AParticle(int p, double s) {
super(p);
spin = s;
}
void newPosition(int delta) {
if (spin < delta)
position = position + delta;
}
}
class BParticle extends Particle {
int charge;
BParticle(int p, int c) {
super(p);
charge = c;
}
}
class CParticle extends BParticle {
boolean strange;
CParticle(int p, int c, boolean s) {
super(p, c);
strange = s;
}
void newPosition(int delta) {
if (strange)
position = position * charge;
}
}
class Inheritance06 {
public static void main(/*String[] args*/) {
Particle[] p = new Particle[3];
p[0] = new AParticle(20, 2.0);
p[1] = new BParticle(30, 3);
p[2] = new CParticle(40, 4, true);
int i = 0;
p[i++].newPosition(10);
p[i++].newPosition(10);
p[i].newPosition(10);
}
}
The follow program declares
Particle
to be abstract
and no objects of that class can be declared. The method newPosition
is also declared abstrct because it doesn't make sense to have a particle that you can't move.Exercise The program does not compile successfully. Why? (Note that in Jeliot, the problem is only found at when animating the program.) Modify the program so that it compiles and executes.
- The objects are created and references to them assigned to elements of the array.
- Method
newPosition
is invoked for the object referenced by each element of the arrayp
. Check that the fields accessed are those of the object referenced by the array element and that the calls are dynamically dispatched to the method appropriate for the type of the object.
Exercise It is possible to declare a nonabsract method in an abstract class. Give an example for this program, and explain why it is a reasonable thing to do.
Equals
Concept There are two concepts of equality in Java: the operator
==
compares primitives types and references, while the methodequals
compares objects. The default implementation of equals
is like ==
, but it can be overridden in any class.Program: Inheritance07A.java
// Learning Object Inheritance07A
// equality (== vs. equals)
class Particle {
int position;
Particle(int p) {
position = p;
}
void newPosition(int delta) {
position = position + delta;
}
}
class AParticle extends Particle {
double spin;
AParticle(int p, double s) {
super(p);
spin = s;
}
void newPosition(int delta) {
if (spin < delta)
position = position + delta;
}
}
class Inheritance07A {
public static void main(/*String[] args*/) {
AParticle a1 = new AParticle(20, 2.0);
AParticle a2 = a1;
AParticle a3 = new AParticle(20, 2.0);
boolean eqop12 = a1 == a2;
boolean eqop13 = a1 == a3;
boolean eqmethod = a1.equals(a3);
}
}
- Object
a1
of typeAParticle
is created. a1
is assigned toa2
using==
.- Object
a3
of typeAParticle
is created with the same values for its fields as the object referenced bya1
. - Evaluating
a1==a2
returns true because they both reference the same object. - Evaluating
a1==a3
returns false because they reference different objects. - Strangely enough, evaluating
a1.equals(a3)
returns false. Although their fields are equal, the default implementation ofequals
is the same as==
!
Exercise Add the follow method to
AParticle
and run the program again. What happens now?public boolean equals(AParticle a) {
return this.position == a.position && this.spin == a.spin;
}
Program: Inheritance07B.java
// Learning Object Inheritance07B
// equality (overloading equals)
class Particle {
int position;
Particle(int p) {
position = p;
}
void newPosition(int delta) {
position = position + delta;
}
}
class BParticle extends Particle {
int charge;
BParticle(int p, int c) {
super(p);
charge = c;
}
public boolean equals(BParticle b) {
return this.position == b.position &&
this.charge == b.charge;
}
}
class CParticle extends BParticle {
boolean strange;
CParticle(int p, int c, boolean s) {
super(p, c);
strange = s;
}
void newPosition(int delta) {
if (strange)
position = position * charge;
}
public boolean equals(CParticle c) {
return this.position == c.position &&
this.charge == c.charge &&
this.strange == c.strange;
}
}
class Inheritance07B {
public static void main(/*String[] args*/) {
BParticle b1 = new BParticle(20, 2);
BParticle b2 = new BParticle(20, 2);
CParticle c1 = new CParticle(20, 2, false);
CParticle c2 = new CParticle(20, 2, true);
boolean eqb1b2 = b1.equals(b2);
boolean eqc1c2 = c1.equals(c2);
boolean eqb1c1 = b1.equals(c1);
boolean eqc1b1 = c1.equals(b1);
}
}
Let us try to override the method
equals
in classes BParticle
and CParticle
; the method returns true if the all fields of the two objects are equal.- Four objects are created: two equal objects
b1
andb2
of typeBParticle
and two unequal objectsc1
andc2
of typeCParticle
. - As expected,
b1.equals(b2)
returns true andc1.equals(c2)
returns false. b1.equals(c1)
returns true: sinceCParticle
is a subclass ofBParticle
, the variablec1
is acceptable as a parameter to the methodequals
declared inBParticle
.c1
is equal tob1
, because we are only comparing the first two fields inherited fromBParticle
and these are equal.
Exercise Explain what happens if you try to evaluate
c1.equals(b1)
.Program: Inheritance07C.java
// Learning Object Inheritance07C
// equality (robust overriding)
class Particle {
int position;
Particle(int p) {
position = p;
}
void newPosition(int delta) {
position = position + delta;
}
}
class BParticle extends Particle {
int charge;
BParticle(int p, int c) {
super(p);
charge = c;
}
}
class CParticle extends BParticle {
boolean strange;
CParticle(int p, int c, boolean s) {
super(p, c);
strange = s;
}
void newPosition(int delta) {
if (strange)
position = position * charge;
}
public boolean equals(Object obj) {
if (obj == null) return false;
if (!(obj instanceof CParticle)) return false;
CParticle c = (CParticle) obj;
return this.position == c.position && this.charge == c.charge &&
this.strange == c.strange;
}
}
class Inheritance07C {
public static void main(/*String[] args*/) {
BParticle b1 = new BParticle(20, 2);
CParticle c1 = new CParticle(20, 2, false);
CParticle c2 = new CParticle(20, 2, true);
CParticle c3 = new CParticle(20, 2, false);
boolean eqc1null = c1.equals(null);
boolean eqc1b1 = c1.equals(b1);
boolean eqc1c2 = c1.equals(c2);
boolean eqc1c3 = c1.equals(c3);
}
}
It would be unusual for two objects to be considered equal if they are of different types, even if one type is a subclass of another. In fact,
public boolean equals(CParticle c)
does not override the method equals
in BParticle
, because an overriding method must have the same signature as the overridden method.The method
equals
is declared in the root class Object
as: public boolean equals(Object obj)
and this is the method that must be overridden. This program shows the correct technique:- Since the parameter can now be any object, a check is first made that the parameter is not
null
. - Similarly, a check is made that the parameter is of the same type as this object.
- Now that we know that the parameter is actually of this type, it can be cast from
Object
to the type. - Only then is class-specific code performed—usually a field-by-field comparison.
Trace the execution of the program:
- Four objects are created: one object
b1
of typeBParticle
and three objectsc1
,c2
andc3
of typeCParticle
. - Clearly, comparing
c1
tonull
orb1
returns false. - Field-by-field comparisons are used if the parameter is of type
CParticle
:c1.equals(c2)
returns false andc1.equals(c3)
returns true.
Exercise Move the declaration of
equals
to class BParticle
, changing the code as needed. What now is the value of c1.equals(b1)
? Explain.Clone
Concept Assigning a variable containing a reference to another of the same type merely copies the reference so that two fields refer to the same object. The method
clone
is used to copy the content of an object into a new one. clone
is defined in class Object
and can be overridden in any class definition.Program: Inheritance08.java
// Learning Object Inheritance08
// clone
class Particle implements Cloneable {
int position;
Particle(int p) {
position = p;
}
void newPosition(int delta) {
position = position + delta;
}
protected Object clone() {
try {
Particle p = (Particle) super.clone();
p.newPosition(10);
return p;
}
catch (CloneNotSupportedException e) {
e.printStackTrace();
throw new Error();
}
}
}
class Inheritance08 {
public static void main(String[] args) {
Particle p1 = new Particle(20);
Particle p2 = p1;
p1.newPosition(10);
System.out.println(p1.position);
System.out.println(p2.position);
Particle p3 = (Particle) p1.clone();
System.out.println(p1.position);
System.out.println(p3.position);
p3.newPosition(10);
System.out.println(p1.position);
System.out.println(p3.position);
}
}
clone
is overridden in class Particle
. The class must implement the interface Cloneable
, the method of the superclass should be called, and we have to take into account that the method might raise an exception. The method returns the object returned by superclass method after calling newPosition
.- An object of class
Particle
is allocated and its reference assigned to the fieldp1
. - An assignment statement copies this reference to the field
p2
. Check that they have the same value. - The method
newPosition
is called onp1
, but the value ofp2.position
is also changed, showing that the two fields point to the same object. - An object of class
Particle
is obtained by callingp1.clone()
and its reference assigned to the fieldp3
. Sinceclone
returns a value of typeObject
, it must be cast to typeParticle
before the assignment. Check that the objects referenced byp1
andp3
have different values. - Calling
p3.newPosition
changes only the field in the object referenced byp3
and not the separate object referenced byp1
.
Exercise The method
clone
can perform arbitrary computation. Modify the program so that new objects are initialized with the absolute value of the field of the object that is being cloned.Overloading vs. overriding
ConceptOverloading is the use of the same method name with a different parameter signature. Overriding is the use in a subclass of the same method name with the same parameter signature as a method of the superclass.
Program: Inheritance09.java
// Learning Object Inheritance09
// overloading vs. overriding
class Particle {
int position;
Particle(int p) {
position = p;
}
void newPosition(int delta) {
position = position + delta;
}
}
class AParticle extends Particle {
double spin;
AParticle(int p, double s) {
super(p);
spin = s;
}
void newPosition(int delta) {
if (spin < delta)
position = position + delta;
}
void newPosition(double delta) {
if (position < delta)
spin = spin + delta;
}
}
class Inheritance09 {
public static void main(/*String[] args*/) {
Particle p = new Particle(10);
AParticle a1 = new AParticle(20, -1.0);
AParticle a2 = new AParticle(20, -1.0);
p.newPosition(10);
int pPosition = p.position;
a1.newPosition(10);
int a1Position = a1.position;
a2.newPosition(10.0);
int a2Position = a2.position;
}
}
The method
newPosition(int delta)
is declared in Particle
and overridden in AParticle
. It is also overloaded by a method with the same name takes a parameter of typedouble
.- After allocating three objects
p
,a1
anda2
,newPosition
is called on each one. p.newPosition
calls the method declared in classParticle
.a1.newPosition
calls the method declared in classAParticle
that overrides the method inParticle
.a2.newPosition
calls the overloaded method because the actual parameter is of typedouble
.
No comments:
Post a Comment