Activity 8.3.1.
Run the following code. Can you change what the cow and cat say?
toString() method. Moreover, we want to design this collection of classes so that it is extensibleβthat is, so that we can continue to add new animals to our menagerie without having to change any of the code in the other classes.
Animal class is an abstract class. Thatβs why its name is italicized in the UML diagram. The reason that this class is abstract is because its speak() method is an abstract method, which is a method definition that does not contain an implementation. That is, the method definition contains just the methodβs signature, not its body. Any class that contains an abstract method, must itself be declared abstract.

Animal class:
public abstract class Animal {
protected String kind; // Cow, pig, cat, etc.
public Animal() { }
public String toString() {
return "I am a " + kind + " and I go " + speak();
}
public abstract String speak(); // Abstract method
}
speak()) and the abstract class. Because one or more of its methods is not implemented, an abstract class cannot be instantiated. That is, you cannot say:
Animal animal = new Animal(); // Error: Animal is abstract
Animal class a constructor. If we had left this off, Java would have supplied a default constructor that would be invoked when Animal subclasses are created.
abstract method must be declared an abstract class.
abstract class cannot be instantiated. It must be subclassed.
abstract class may be instantiated only if it implements all of the superclassβs abstract methods. A subclass that implements only some of the abstract methods must itself be declared abstract.
abstract even it contains no abstract methods. It could, for example, contain instance variables that are common to all its subclasses.
toString() method calls the abstract speak() method. The reason that this works in Java is due to the dynamic binding mechanism. The polymorphic speak() method will be defined in the various Animal subclasses. When the Animal.toString() method is called, Java will decide which actual speak() method to call based on what subclass of Animal is involved.
Animal subclasses.public class Cat extends Animal {
public Cat() {
kind = "cat";
}
public String speak() {
return "meow";
}
}
public class Cow extends Animal {
public Cow() {
kind = "cow";
}
public String speak() {
return "moo";
}
}
Animal class and provides its own constructor and its own implementation of the speak() method. Note that in their respective constructors, we can refer to the kind instance variable, which is inherited from the Animal class. By declaring kind as a protected variable, it is inherited by all Animal subclasses but hidden from all other classes. On the other hand, if kind had been declared public, it would be inherited by subclassesb ut it would also be accessible to every other class, a violation of the information hiding principle.
main() method:
Animal animal = new Cow();
System.out.println(animal.toString()); // A cow goes moo
animal = new Cat();
System.out.println(animal.toString()); // A cat goes meow
Cow object and then invoke its (inherited) toString() method. It returns, βI am a cow and I go moo.β We then create a Cat object and invoke its (inherited) toString() method, which returns, βI am a cat and I go meow.β
speak() at run time in each case. The invocation of the abstract speak() method in the Animal.toString() method is a second form of polymorphism.
Animal hierarchy. We can define and use completely new Animal subclasses without redefining or recompiling the rest of the classes in the hierarchy. Javaβs dynamic binding mechanism enables the Animal.toString() method to determine the type of Animal at run time subclass so that it will call the appropriate speak() method for that type of Animal.
Animal subclass with its own speaking method. A Cow would have a moo() method; a Cat would have a meow() method; and so forth. Given this design, we could use a switch statement to select the appropriate method call. For example, consider the following method definition:
public String talk(Animal a) {
if (a instanceof Cow)
return "I am a " + kind + " and I go " + a.moo();
else if (a instanceof Cat)
return "I am a " + kind + " and I go " + a.meow();
else
return "I don't know what I am";
}
instanceof operator, which is a built-in boolean operator. It returns true if the object on its left-hand side is an instance of the class on its right-hand side.
talk() method would produce more or less the same result as our polymorphic approach. If you call talk(new Cow()), it will return βI am a cow and I go moo.β However, with this design, it is not possible to extend the Animal hierarchy without rewriting and recompiling the talk() method. Imagine how unwieldy this would become if we want to add many different animals.
Animal subclass named Pig, which goes βoink.β
final variables).
ActionListener interface in ChapterΒ 4.
speak() method in the animal example. The difference between implementing a method from an interface and from an abstract superclass is that a subclass extends an abstract superclass but it implements an interface.
speak() as an abstract method within the Animal superclass, we will define it as an abstract method in the Speakable interface (Fig. FigureΒ 8.3.3).
public interface Speakable {
public String speak();
}
public class Animal {
protected String kind; // Cow, pig, cat, etc.
public Animal() { }
public String toString() {
return "I am a " + kind + " and I go " +
((Speakable)this).speak();
}
}
Speakable interface.Animal and the previous definition. This version no longer contains the abstract speak() method. Therefore, the class itself is not an abstract class. However, because the speak() method is not declared in this class, we cannot call the speak() method in the toString() method, unless we cast this object into a Speakable object.
(int) and (char). Here, we use it to specify the actual type of some object. In this toString() example, this object is some type of Animal subclass, such as a Cat. The cast operation, (Speakable), changes the objectβs actual type to Speakable, which syntactically allows its speak() method to be called.
Animal subclasses will now extend the Animal class and implement the Speakable interface:
public class Cat extends Animal implements Speakable {
public Cat() { kind = "cat"; }
public String speak() {
return "meow";
}
}
public class Cow extends Animal implements Speakable {
public Cow() { kind = "cow"; }
public String speak() {
return "moo";
}
}
speak() method.
Animal.toString() class
((Speakable)this).speak();
this object into a Speakable object. The cast is required because the Animal class does not have a sleep() method. Therefore, in order to invoke speak() on an object from one of the Animal subclasses, the object must actually be a Speakable.
Cat, by virtue of extending the Animal class and implementing the Speakable interface, is both an Animal and a Speakable.
Cow and Cat subclasses, the following code segment will produce the same results as in the previous section.
Animal animal = new Cow();
System.out.println(animal.toString()); // A cow goes moo
animal = new Cat();
System.out.println(animal.toString()); // A cat goes meow
Pig to the hierarchy using the interface implementation.
TwoPlayerGame class hierarchy later in this chapter.