Thinking in Java Fourth Edition Bruce Eckel Chapter 8 Exercise 4

Exercise 4: Create an abstract class with no methods. Derive a class and add a method. Create a static method that takes a reference to the base class, downcasts it to the derived class, and calls the method. In main( ), demonstrate that it works. Now put the abstract declaration for the method in the base class, thus eliminating the need for the downcast.

Solution:

abstract class NoMethods
{
}
class Extended1 extends NoMethods
{
    public void f()
    {
        System.out.println("Extended1.f");
    }
}
abstract class WithMethods
{
    abstract public void f();
}
class Extended2 extends WithMethods
{
    public void f()
    {
        System.out.println("Extended2.f");
    }
}
public class E04
{
    public static void test1(NoMethods nm)
    {
        // Must downcast to access f():
        ((Extended1)nm).f();
    }
    public static void test2(WithMethods wm)
    {
        // No downcast necessary:
        wm.f();
    }
    public static void main(String args[])
    {
        NoMethods nm = new Extended1();
        test1(nm);
        WithMethods wm = new Extended2();
        test2(wm);
    }
}

Output:

Thinking in Java Fourth Edition Bruce Eckel Chapter 8 Exercise 3

Exercise 3: Create a base class with an abstract print( ) method that is overridden in a derived class. The overridden version of the method prints the value of an int variable defined in the derived class. At the point of definition of this variable, give it a nonzero value. In the base-class constructor, call this method. In main( ), create an object of the derived type, and then call its print( ) method. Explain the results.

Solution:

abstract class BaseWithPrint
{
public BaseWithPrint()
{
print();
}
public abstract void print();
}
class DerivedWithPrint extends BaseWithPrint
{
int i = 47;
public void print()
{
System.out.println("i = " + i);
}
}
public class E03
{
public static void main(String args[])
{
DerivedWithPrint dp = new DerivedWithPrint();
dp.print();
}
}

Ouput:

Thinking in Java Fourth Edition Bruce Eckel Chapter 8 Exercise 2

Exercise 2: Create a class as abstract without including any abstract methods and verify that you cannot create any instances of that class.

Solution

public class Ex2
{
    public static void main(String[] args)
    {
        // Nogo1 and Nogo2 cannot be instantiated:
        // Nogo1 ng1 = new Nogo1();
        // Nogo2 ng2 = new Nogo2();
        // But Nogo1() constructor called during instatiation of child:
        Go1 g1 = new Go1();
    }
}
class Go1 extends Nogo1
{
    Go1()
    {
        System.out.println("Go1()");
    }
}
abstract class Nogo1
{
    Nogo1()
    {
        System.out.println("Nogo1()");
    }
}
abstract class Nogo2
{

}

Output:

Thinking in Java Fourth Edition Bruce Eckel Chapter 8 Exercise 1

Exercise 1: Modify Exercise 9 in the previous chapter so that Rodent is an abstract class. Make the methods of Rodent abstract whenever possible

Solution:

package interfaces.rodent;

import java.util.*;

public class RandomRodentGenerator1 {
    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 interfaces.rodent;

abstract class Rodent {
    private String name = "Rodent";

    abstract protected void eat();

    abstract protected void run();

    abstract protected void sleep();

    abstract public String toString();
}

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 Rodent1 {
    private static RandomRodentGenerator1 gen = new RandomRodentGenerator1();

    public static void main(String[] args) {
        // Error: cannot instantiate abstract class:
        // Rodent x = new Rodent();
        // But OK to create array to be downcast to derived objects:
        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 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: