Thursday, January 13, 2011

Learning Objects for Inheritance in Java

Summary: These learning objects concern inheritance in Java. One class can be derived from another, inheriting its fields and methods.







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.
TABLE 1
LOTopicJava Files (.java)Prerequisites
"Inheriting fields"Inheriting fieldsInheritance01 
"Inheriting methods"Inheriting and overriding methodsInheritance021
"Dynamic dispatching"Dynamic dispatchingInheritance032
"Downcasting"DowncastingInheritance043
"Heterogeneous data structures"Heterogeneous data structuresInheritance054
"Abstract classes"Abstract classesInheritance065
"Equals"EqualsInheritance07A, B, C2
"Clone"CloneInheritance082
"Overloading vs. overriding"Overloading vs. overridingInheritance093
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 from Particle and adds the field spin. The method newPosition is overridden.
  • BParticle is derived directly from Particle and adds the field charge. The method newPosition is not overridden.
  • CParticle is derived directly from BParticle and thus indirectly from AParticle. It adds the field strange; the method newPosition 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 of position is assigned to a variable.
  • Check that the call on p calls the method defined in class Particle.
  • Check that the call on a calls the method defined in the class AParticle; this method overrides the method declared in class Particle.
  • Check that the call on b calls the method defined in the superclass Particle; since the method was not overridden in BParticle, the method called is the one inherited from the superclass.
  • Check that the call on c calls the method defined in the class BParticle; this method overrides the method declared in class Particle.
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 method newPosition is invoked using p. The modified value of position is assigned to a variable.
  • Check that the call on p invokes the method defined in class Particle.
  • After assigning a to p, check that the call invokes the method defined in the class AParticle; this method overrides the method declared in class Particle. Although the type of p is class Particle, it holds a reference to an object whose type is class AParticle 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 to p, check that the call invokes the method defined in the superclass Particle; since the method was not overridden in BParticle, the method called is the one inherited from the superclass.
  • After assigning c to p, check that the call invokes the method defined in the class CParticle; this method overrides the method declared in class Particle. Although the type of p is class Particle, it holds a reference to an object whose type is class CParticle 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 to p. Note that the arrows from the representation of both variables point to the same object of type AParticle, and that the other object is garbage.
  • When the value of p.position is accessed, it refers to the value that is in a.position.
  • The reference in p can be cast to the type AParticle and assigned to a. Although p is declared to hold references to objects of type Particle, the object was really of the subclass AParticle.
  • Both the fields position and spin can be accessed through a.
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 array p. 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 array p. 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 type AParticle is created.
  • a1 is assigned to a2 using ==.
  • Object a3 of type AParticle is created with the same values for its fields as the object referenced by a1.
  • 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 of equals 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 and b2 of type BParticle and two unequal objects c1 and c2 of type CParticle.
  • As expected, b1.equals(b2) returns true and c1.equals(c2) returns false.
  • b1.equals(c1) returns true: since CParticle is a subclass of BParticle, the variable c1 is acceptable as a parameter to the method equals declared in BParticle.c1is equal to b1, because we are only comparing the first two fields inherited from BParticle 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 type BParticle and three objects c1c2 and c3 of type CParticle.
  • Clearly, comparing c1 to null or b1 returns false.
  • Field-by-field comparisons are used if the parameter is of type CParticlec1.equals(c2) returns false and c1.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 field p1.
  • An assignment statement copies this reference to the field p2. Check that they have the same value.
  • The method newPosition is called on p1, but the value of p2.position is also changed, showing that the two fields point to the same object.
  • An object of class Particle is obtained by calling p1.clone() and its reference assigned to the field p3. Since clone returns a value of type Object, it must be cast to type Particle before the assignment. Check that the objects referenced by p1 and p3 have different values.
  • Calling p3.newPosition changes only the field in the object referenced by p3 and not the separate object referenced by p1.
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 pa1 and a2newPosition is called on each one.
  • p.newPosition calls the method declared in class Particle.
  • a1.newPosition calls the method declared in class AParticle that overrides the method in Particle.
  • a2.newPosition calls the overloaded method because the actual parameter is of type double.

No comments:

Post a Comment

Related Posts Plugin for WordPress, Blogger...

java

Popular java Topics