Thinking in Java Fourth Edition Bruce Eckel Chapter 7 Exercise 17

Exercise 17: Using the Cycle hierarchy from Exercise 1, add a balance( ) method to Unicycle and Bicycle, but not to Tricycle. Create instances of all three types and upcast them to an array of Cycle. Try to call balance( ) on each element of the array and observe the results. Downcast and call balance( ) and observe what happens.

Solution:

package polymorphism.biking;

class Cycle {
    private String name = "Cycle";

    public static void travel(Cycle c) {
        System.out.println("Cycle.ride() " + c);
    }

    public String toString() {
        return this.name;
    }
}

class Unicycle extends Cycle {
    private String name = "Unicycle";

    public void balance() {
        System.out.println("Balance Unicycle");
    }

    public String toString() {
        return this.name;
    }
}

class Bicycle extends Cycle {
    private String name = "Bicycle";

    public void balance() {
        System.out.println("Balance Bicycle");
    }

    public String toString() {
        return this.name;
    }

}

class Tricycle extends Cycle {
    private String name = "Tricycle";

    public String toString() {
        return this.name;
    }
}

public class Biking17 {
    public static void main(String[] args) {
        Cycle[] ride = { new Unicycle(), new Bicycle(), new Tricycle(), };
        // Compile time error: cannot find balance() method in Cycle:
        // for(Cycle c : ride) {
        // c.balance();
        // }
        ((Unicycle) ride[0]).balance();
        ((Bicycle) ride[1]).balance();
        // Compile time error: no balance() in Tricycle:
        // ((Tricycle)ride[2]).balance();
        // RTTI: ClassCastException: Tricycle cannot be cast to Bicycle:
        // ((Bicycle)ride[2]).balance();
    }
}

Output:

Thinking in Java Fourth Edition Bruce Eckel Chapter 7 Exercise 16

Exercise 16: Following the example in Transmogrify.java, create a Starship class containing an AlertStatus reference that can indicate three different states. Include methods to change the states.

Solution:

class AlertStatus {
    public void alert() {
    }
}

class NormalAlert extends AlertStatus {
    public void alert() {
        System.out.println("AlertStatus Normal");
    }
}

class HighAlert extends AlertStatus {
    public void alert() {
        System.out.println("AlertStatus High");
    }
}

class MaximumAlert extends AlertStatus {
    public void alert() {
        System.out.println("AlertStatus Maximum");
    }
}

class Starship {
    private AlertStatus alertStatus = new NormalAlert();

    public void normalAlert() {
        alertStatus = new NormalAlert();
    }

    public void highAlert() {
        alertStatus = new HighAlert();
    }

    public void maximumAlert() {
        alertStatus = new MaximumAlert();
    }

    public void checkAlertStatus() {
        alertStatus.alert();
    }
}

public class Transmogrify16 {
    public static void main(String[] args) {
        Starship ss = new Starship();
        ss.checkAlertStatus();
        ss.highAlert();
        ss.checkAlertStatus();
        ss.maximumAlert();
        ss.checkAlertStatus();
        ss.normalAlert();
        ss.checkAlertStatus();
    }
}

Output:

Thinking in Java Fourth Edition Bruce Eckel Chapter 7 Exercise 15

Exercise 15: Add a RectangularGlyph to PolyConstructors.java and demonstrate the problem described in this section.

Solution:

class Glyph {
    void draw() {
        System.out.println("Glyph.draw()");
    }

    Glyph() {
        System.out.println("Glyph() before draw()");
        draw();
        System.out.println("Glyph() after draw()");
    }
}

class RoundGlyph extends Glyph {
    private int radius = 1;

    RoundGlyph(int r) {
        radius = r;
        System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius);
    }

    void draw() {
        System.out.println("RoundGlyph.draw(), radius = " + radius);
    }
}

class RectangularGlyph extends Glyph {
    private int length = 2;
    private int width = 4;

    RectangularGlyph(int l, int w) {
        length = l;
        width = w;
        System.out.println("RectangularGlyph.RectangularGlyph(), length = " + length + ", width = " + width);
    }

    void draw() {
        System.out.println("RectangularGlyph.draw(), length = " + length + ", width = " + width);
    }
}

public class PolyConstructors15 {
    public static void main(String[] args) {
        new RoundGlyph(5);
        new RectangularGlyph(3, 6);
    }
}

Output:

Thinking in Java Fourth Edition Bruce Eckel Chapter 7 Exercise 14

Exercise 14: Modify Exercise 12 so that one of the member objects is a shared object with reference counting, and demonstrate that it works properly.

Solution:

package polymorphism.rodent;

public class Shared {
    private int refcount = 0;
    private static long counter = 0;
    private final long id = counter++;

    public Shared() {
        System.out.println("Creating " + this);
    }

    public void addRef() {
        refcount++;
    }

    public String toString() {
        return "Shared " + id;
    }

    public void showRefcount() {
        System.out.println("refcount = " + refcount);
    }
}
package polymorphism.rodent;

import java.util.*;

public class RandomRodentGenerator {
    // same shared object to be passed to every Rodent:
    protected Shared shared = new Shared();
    private Random rand = new Random();

    public Rodent next() {
        switch (rand.nextInt(3)) {
        default:
        case 0:
            return new Mouse(shared);
        case 1:
            return new Rat(shared);
        case 2:
            return new Squirrel(shared);
        }
    }
}
package polymorphism.rodent;

class Characteristic {
    private String s;

    Characteristic(String s) {
        this.s = s;
        System.out.println("Creating Characteristic " + s);
    }
}

class Description {
    private String s;

    Description(String s) {
        this.s = s;
        System.out.println("Creating Description " + s);
    }
}

class Rodent {
    private String name = "Rodent";
    private Shared shared;
    private static long counter = 0;
    private final long id = counter++;
    private Characteristic c = new Characteristic("has tail");
    private Description d = new Description("small mammal");

    Rodent(Shared shared) {
        System.out.println("Rodent() " + id);
        this.shared = shared;
        this.shared.addRef();
    }

    protected void eat() {
        System.out.println("Rodent.eat()");
    }

    protected void run() {
        System.out.println("Rodent.run()");
    }

    protected void sleep() {
        System.out.println("Rodent.sleep()");
    }

    public String toString() {
        return name + " " + id;
    }
}

class Mouse extends Rodent {
    private String name = "Mouse";
    private Characteristic c = new Characteristic("likes cheese");
    private Description d = new Description("nocturnal");

    Mouse(Shared shared) {
        super(shared);
        System.out.println("Mouse()");
    }

    protected void eat() {
        System.out.println("Mouse.eat()");
    }

    protected void run() {
        System.out.println("Mouse.run()");
    }

    protected void sleep() {
        System.out.println("Mouse.sleep()");
    }

    public String toString() {
        return name + ", " + super.toString();
    }
}

class Rat extends Rodent {
    private String name = "Rat";
    private Characteristic c = new Characteristic("larger");
    private Description d = new Description("black");

    Rat(Shared shared) {
        super(shared);
        System.out.println("Rat()");
    }

    protected void eat() {
        System.out.println("Rat.eat()");
    }

    protected void run() {
        System.out.println("Rat.run()");
    }

    protected void sleep() {
        System.out.println("Rat.sleep()");
    }

    public String toString() {
        return name + ", " + super.toString();
    }
}

class Squirrel extends Rodent {
    private String name = "Squirrel";
    private Characteristic c = new Characteristic("climbs trees");
    private Description d = new Description("likes nuts");

    Squirrel(Shared shared) {
        super(shared);
        System.out.println("Squirrel()");
    }

    protected void eat() {
        System.out.println("Squirrel.eat()");
    }

    protected void run() {
        System.out.println("Squirrel.run()");
    }

    protected void sleep() {
        System.out.println("Squirrel.sleep()");
    }

    public String toString() {
        return name + ", " + super.toString();
    }
}

public class Rodent14 {
    private static RandomRodentGenerator gen = new RandomRodentGenerator();

    public static void main(String[] args) {
        Rodent[] rodents = new Rodent[5];
        for (Rodent r : rodents) {
            r = gen.next();
            System.out.println(r);
        }
        gen.shared.showRefcount();
    }
}

Output:

Thinking in Java Fourth Edition Bruce Eckel Chapter 7 Exercise 13

Exercise 13: Add a finalize( ) method to ReferenceCounting.java to verify the termination condition (see the Initialization & Cleanup chapter).

Solution:

class Shared {
    private int refcount = 0;
    private static long counter = 0;
    private final long id = counter++;

    public Shared() {
        System.out.println("Creating " + this);
    }

    public void addRef() {
        refcount++;
    }

    protected void finalize() {
        if (refcount > 0)
            System.out.println("Error: " + refcount + " Shared " + id + " objects in use");
    }

    protected void dispose() {
        if (--refcount == 0)
            System.out.println("Disposing " + this);
    }

    public String toString() {
        return "Shared " + id;
    }
}

class Composing {
    private Shared shared;
    private static long counter = 0;
    private final long id = counter++;

    public Composing(Shared shared) {
        System.out.println("Creating " + this);
        this.shared = shared;
        this.shared.addRef();
    }

    protected void dispose() {
        System.out.println("Disposing " + this);
        shared.dispose();
    }

    public String toString() {
        return "Composing " + id;
    }
}

public class ReferenceCounting13 {
    public static void main(String[] args) {
        Shared shared = new Shared();
        Composing[] composing = { new Composing(shared), new Composing(shared), new Composing(shared),
                new Composing(shared), new Composing(shared) };
        for (Composing c : composing)
            c.dispose();
        Composing compTest = new Composing(shared);
        Composing compTest2 = new Composing(shared);
        // Test finalize():
        shared.finalize();
        Shared sharedTest = new Shared();
        Composing compTest3 = new Composing(sharedTest);
        // Test sharedTest finalize():
        sharedTest.finalize();
    }
}

Output:

Thinking in Java Fourth Edition Bruce Eckel Chapter 7 Exercise 12

Exercise 12: Modify Exercise 9 so that it demonstrates the order of initialization of the base classes and derived classes. Now add member objects to both the base and derived classes and show the order in which their initialization occurs during construction.

package polymorphism.rodent;

import java.util.*;

public class RandomRodentGenerator {
    private Random rand = new Random();

    public Rodent next() {
        switch (rand.nextInt(3)) {
        default:
        case 0:
            return new Mouse();
        case 1:
            return new Rat();
        case 2:
            return new Squirrel();
        }
    }
}
package polymorphism.rodent;

class Characteristic {
    private String s;

    Characteristic(String s) {
        this.s = s;
        System.out.println("Creating Characteristic " + s);
    }
}

class Description {
    private String s;

    Description(String s) {
        this.s = s;
        System.out.println("Creating Description " + s);
    }
}

class Rodent {
    private String name = "Rodent";
    private Characteristic c = new Characteristic("has tail");
    private Description d = new Description("small mammal");

    Rodent() {
        System.out.println("Rodent()");
    }

    protected void eat() {
        System.out.println("Rodent.eat()");
    }

    protected void run() {
        System.out.println("Rodent.run()");
    }

    protected void sleep() {
        System.out.println("Rodent.sleep()");
    }

    public String toString() {
        return name;
    }
}

class Mouse extends Rodent {
    private String name = "Mouse";
    private Characteristic c = new Characteristic("likes cheese");
    private Description d = new Description("nocturnal");

    Mouse() {
        System.out.println("Mouse()");
    }

    protected void eat() {
        System.out.println("Mouse.eat()");
    }

    protected void run() {
        System.out.println("Mouse.run()");
    }

    protected void sleep() {
        System.out.println("Mouse.sleep()");
    }

    public String toString() {
        return name;
    }
}

class Rat extends Rodent {
    private String name = "Rat";
    private Characteristic c = new Characteristic("larger");
    private Description d = new Description("black");

    Rat() {
        System.out.println("Rat()");
    }

    protected void eat() {
        System.out.println("Rat.eat()");
    }

    protected void run() {
        System.out.println("Rat.run()");
    }

    protected void sleep() {
        System.out.println("Rat.sleep()");
    }

    public String toString() {
        return name;
    }
}

class Squirrel extends Rodent {
    private String name = "Squirrel";
    private Characteristic c = new Characteristic("climbs trees");
    private Description d = new Description("likes nuts");

    Squirrel() {
        System.out.println("Squirrel()");
    }

    protected void eat() {
        System.out.println("Squirrel.eat()");
    }

    protected void run() {
        System.out.println("Squirrel.run()");
    }

    protected void sleep() {
        System.out.println("Squirrel.sleep()");
    }

    public String toString() {
        return name;
    }
}

public class Rodent12 {
    private static RandomRodentGenerator gen = new RandomRodentGenerator();

    public static void main(String[] args) {
        Rodent[] rodents = new Rodent[10];
        for (Rodent r : rodents) {
            r = gen.next();
            System.out.println(r);
        }
    }
}

Output:

Thinking in Java Fourth Edition Bruce Eckel Chapter 7 Exercise 11

Exercise 11: Add class Pickle to Sandwich.java.

package polymorphism;

class Meal {
    Meal() {
        System.out.println("Meal()");
    }
}

class Bread {
    Bread() {
        System.out.println("Bread()");
    }
}

class Cheese {
    Cheese() {
        System.out.println("Cheese()");
    }
}

class Lettuce {
    Lettuce() {
        System.out.println("Lettuce()");
    }
}

class Pickle {
    Pickle() {
        System.out.println("Pickle()");
    }
}

class Lunch extends Meal {
    Lunch() {
        System.out.println("Lunch()");
    }
}

class PortableLunch extends Lunch {
    PortableLunch() {
        System.out.println("PortableLunch()");
    }
}

public class Sandwich11 extends PortableLunch {
    private Bread b = new Bread();
    private Cheese c = new Cheese();
    private Pickle p = new Pickle();
    private Lunch l = new Lunch();

    public Sandwich11() {
        System.out.println("Sandwich()");
    }

    public static void main(String[] args) {
        new Sandwich11();
    }
}

Output:

Thinking in Java Fourth Edition Bruce Eckel Chapter 7 Exercise 10

Exercise 10: Create a base class with two methods. In the first method, call the second method. Inherit a class and override the second method. Create an object of the derived class, upcast it to the base type, and call the first method. Explain what happens.

class A {
    protected void f() {
        System.out.println("A.f()");
        this.g();
    }

    protected void g() {
        System.out.println("A.g()");
    }
}

class B extends A {
    @Override
    protected void g() {
        System.out.println("B.g()");
    }
}

public class Ex10 {
    public static void main(String[] args) {
        B b = new B();
        // automatic upgrade to base-class to call base-class method A.f()
        // which,by polymorphism, will call the derived-class method B.g():
        b.f();
    }
}

Output:

Thinking in Java Fourth Edition Bruce Eckel Chapter 7 Exercise 9

Exercise 9: Create an inheritance hierarchy of Rodent: Mouse, Gerbil, Hamster, etc. In the base class, provide methods that are common to all Rodents, and override these in the derived classes to perform different behaviors depending on the specific type of Rodent. Create an array of Rodent, fill it with different specific types of Rodents, and call your base-class methods to see what happens.

package polymorphism.rodent;

import java.util.*;

public class RandomRodentGenerator {
    private Random rand = new Random();

    public Rodent next() {
        switch (rand.nextInt(3)) {
        default:
        case 0:
            return new Mouse();
        case 1:
            return new Rat();
        case 2:
            return new Squirrel();
        }
    }
}
package polymorphism.rodent;

class Rodent {
    private String name = "Rodent";

    protected void eat() {
        System.out.println("Rodent.eat()");
    }

    protected void run() {
        System.out.println("Rodent.run()");
    }

    protected void sleep() {
        System.out.println("Rodent.sleep()");
    }

    public String toString() {
        return name;
    }
}

class Mouse extends Rodent {
    private String name = "Mouse";

    protected void eat() {
        System.out.println("Mouse.eat()");
    }

    protected void run() {
        System.out.println("Mouse.run()");
    }

    protected void sleep() {
        System.out.println("Mouse.sleep()");
    }

    public String toString() {
        return name;
    }
}

class Rat extends Rodent {
    private String name = "Rat";

    protected void eat() {
        System.out.println("Rat.eat()");
    }

    protected void run() {
        System.out.println("Rat.run()");
    }

    protected void sleep() {
        System.out.println("Rat.sleep()");
    }

    public String toString() {
        return name;
    }
}

class Squirrel extends Rodent {
    private String name = "Squirrel";

    protected void eat() {
        System.out.println("Squirrel.eat()");
    }

    protected void run() {
        System.out.println("Squirrel.run()");
    }

    protected void sleep() {
        System.out.println("Squirrel.sleep()");
    }

    public String toString() {
        return name;
    }
}

public class Rodent9 {
    private static RandomRodentGenerator gen = new RandomRodentGenerator();

    public static void main(String[] args) {
        Rodent[] rodents = new Rodent[10];
        for (Rodent r : rodents) {
            r = gen.next();
            System.out.println(r + ": ");
            r.eat();
            r.run();
            r.sleep();
        }
    }
}

Output:

Thinking in Java Fourth Edition Bruce Eckel Chapter 7 Exercise 8

Exercise 8: Modify Music3.java so that it randomly creates Instrument objects the way Shapes.java does.

package polymorphism.music8;

import java.util.*;

public class RandomInstrumentGenerator {
    private Random rand = new Random();

    public Instrument next() {
        switch (rand.nextInt(7)) {
        default:
        case 0:
            return new Wind();
        case 1:
            return new Percussion();
        case 2:
            return new Stringed();
        case 3:
            return new Keyboard();
        case 4:
            return new Brass();
        case 5:
            return new Woodwind();
        case 6:
            return new Piano();
        }
    }
}
package polymorphism.music;

public enum Note {
    MIDDLE_C, C_SHARP, B_FLAT; // Etc.
}
package polymorphism.music8;

import polymorphism.music.Note;

class Instrument {
    void play(Note n) {
        System.out.println("Instrument.play() " + n);
    }

    public String toString() {
        return "Instrument";
    }

    void adjust() {
        System.out.println("Adjusting Instrument");
    }
}

class Wind extends Instrument {
    void play(Note n) {
        System.out.println("Wind.play() " + n);
    }

    public String toString() {
        return "Wind";
    }

    void adjust() {
        System.out.println("Adjusting Wind");
    }
}

class Percussion extends Instrument {
    void play(Note n) {
        System.out.println("Percussion.play() " + n);
    }

    public String toString() {
        return "Percussion";
    }

    void adjust() {
        System.out.println("Adjusting Percussion");
    }
}

class Stringed extends Instrument {
    void play(Note n) {
        System.out.println("Stringed.play() " + n);
    }

    public String toString() {
        return "Stringed";
    }

    void adjust() {
        System.out.println("Adjusting Stringed");
    }
}

class Keyboard extends Instrument {
    void play(Note n) {
        System.out.println("Keyboard.play() " + n);
    }

    public String toString() {
        return "Keyboard";
    }

    void adjust() {
        System.out.println("Adjusting Keyboard");
    }
}

class Brass extends Wind {
    void play(Note n) {
        System.out.println("Brass.play() " + n);
    }

    public String toString() {
        return "Brass";
    }

    void adjust() {
        System.out.println("Adjusting Brass");
    }
}

class Woodwind extends Wind {
    void play(Note n) {
        System.out.println("Woodwind.play() " + n);
    }

    public String toString() {
        return "Woodwind";
    }
}

class Piano extends Keyboard {
    void play(Note n) {
        System.out.println("Piano.play() " + n);
    }

    public String toString() {
        return "Piano";
    }
}

public class Music8 {
    // Doesn't care about type, so new types
    // added to the system still work right:
    public static void tune(Instrument i) {
        // ...
        i.play(Note.MIDDLE_C);
    }

    public static void tuneAll(Instrument[] e) {
        for (Instrument i : e)
            tune(i);
    }

    private static RandomInstrumentGenerator gen = new RandomInstrumentGenerator();

    public static void main(String[] args) {
        // Upcasting during addition to the array:
        Instrument[] orchestra = new Instrument[20];
        // fill up orchestra array wth instruments:
        for (int i = 0; i < orchestra.length; i++)
            orchestra[i] = gen.next();
        tuneAll(orchestra);
        for (Instrument i : orchestra)
            System.out.println(i);
    }
}

Output: