Java 1.4 Game Programming (Wordware Game and Graphics Library)

Inheritance is a very important part of object-oriented programming. It is often a worry for many programmers new to the subject. But fear not, as it is easy to understand in Java. Inheritance is the ability to derive a new class from an existing class. In this case, the existing class is known as the base class, or super class, and the new class is known as the subclass, or derived class of the base class. The subclass then inherits variables and methods from its base class that it can use as if they had been defined in the subclass. However, constructors of the base class are not inherited by its subclasses and neither are certain access attributes, depending on the circumstances. We will look at what is inherited and what isn't later in this section; for now we should understand the fundamentals of inheritance.

The Object Class

Before we begin making our own subclasses, we should understand an essential part of the Java language, the Object class, which should help us to understand inheritance a little better. What if I told you that you have been using inheritance all along in all of the classes that we have created so far? In Java, as we might have mentioned in passing before now, all classes are derived from the Object class by default, though this is hidden from you in your code. The Object class is a member of the java.lang package and is a super class of all classes. This means that all of your classes have inherited members of the Object class. All of the members inherited from Object are methods, and furthermore they are instance methods, which means they only exist when you create an object of your class. At this point, we should look at the most straightforward method inherited from the Object class by all classes in Java: the toString method.

The toString Method

The toString method is used to get a string representation of an object, returning this string representation value of type String. Remember back when we used the toString method in both the Universe class and the Alien class to return what was considered an ideal textual representation for the data stored in objects of those classes. So, for example, the Alien class's toString method returned the Alien string value greeting, as this was all that basically made our Alien objects different from any other Alien object.

The key thing to realize is that this method is initially inherited from the Object class, but in the Universe class and the Alien class we override the toString method, creating our own version of it. Then, when a string representation of an object of these types is required, the overriding toString method that we created is used instead of the toString method in Object. The string representation of the object is required when printing to the screen, where we appeared to be able to print the object.

System.out.println(universe);

This code simply invoked the toString method that we overrode in the Universe class, printing the return value from that method; otherwise it would have printed the value returned from the toString method of the Object class instead, which is a default textual representation of an object that actually consists of the class's name, followed by the hash code of the object, which we do not need to worry about.

We could similarly have printed this data as follows:

System.out.println(universe.toString());

The toString method is a method similar to any other method, but it is also handled as the default string representation of the object.

The following diagram shows the structure of the source code in Listings 4-1 through 4-3, which, in case you've forgotten, contained the classes MainApp, Universe, and Alien.

Figure 4-5:

All classes inherit these methods from Object, as this is always the root class in any class's hierarchy. The following is a list of methods in the Object class with a description of what they do.

protected Object clone()

This method is used to create a copy of an object and returns it as type Object. Only classes that implement the interface Cloneable can be cloned. We will discuss interfaces later on in the chapter.

public boolean equals(Object)

This method returns true if this object is equal to one passed as a parameter. Hence, if this object is the same as that which the parameter currently references, then it returns true.

protected void finalize()

This method is called by the garbage collector when you are finished with your object, that is, when you no longer hold any references to it. Note that the garbage collector is likely not to call this method right away but when it's ready to, which can be some time after any references to this object no longer exist in your program and it is ready to be collected/removed. So if you override this method and provide code that needs to be handled in real time, it is recommended that you realize when your object is lost and handle this code then, instead of when this method is invoked by the JVM. For further information, see the "Garbage Collection and Creating Objects" section in Chapter 12.

public final Class getClass()

Returns a Class object of this object. An object of type Class can be used for such things as finding if an object is an instance of a given class.

public int hashCode()

This method returns a hash code value for the object.

public string toString()

As we mentioned earlier, this method is designed so that each object has a string representation of itself. In most cases this method is overridden in a class to return a desired value.

The methods notify, notifyAll, and the various overloaded wait methods concern the use of threads. These methods are discussed in Chapters 7 and 9, where we utilize these methods to help us make our passive rendering as active as possible. That is some way off, but it'll be worth the wait, so keep reading.

Inheriting Your Own Classes

To derive one of your own classes from another class, use the keyword extends, as in your class extends another class. Returning to our Alien and Human example, we could create a base class Creature and create Alien and Human classes that both extend the Creature class; that is, they are subclasses of the Creature class. In this case the base class must contain members that are true for both Alien and Human objects, as they will inherit these members and then the Alien and Human classes would include extra members that are specific to them and not to other Creature types. The following example (Listings 4-4 through 4-7) contains four classes: Beings (main class), Creature, Alien, and Human. Let's take a look at the code for this example.

Note 

There is no multiple inheritance in Java. Any class can only extend one other class. A means of working around this issue is using interfaces, as we shall see toward the end of this chapter.

Code Listing 4-4: Creatures.java

public class Creature { public Creature(String greeting) { setGreeting(greeting); } public void setGreeting(String greeting) { this.greeting = greeting; } public String getGreeting() { return greeting; } public void speak() { System.out.println("Creature says: " + greeting); } private String greeting; }

This class is the base class for a creature in our program, as we assume that all varieties of creatures will require a greeting variable. In this class we require the public methods setGreeting and getGreeting, as the greeting variable is set to private, meaning that it is not itself inherited by any subclasses of Creature but those public methods are inherited and can be used to access greeting, which still exists but is just not inherited. We will discuss this a little later also.

Code Listing 4-5: Alien.java

public class Alien extends Creature { public Alien(String greeting) { super(greeting); } public void speak() { System.out.println("Alien says: " + getGreeting()); } }

As you can see, the Alien class uses the keyword extends after its name declaration followed by the base class Creature that it extends. The Alien class therefore inherits the public methods setGreeting and getGreeting from the Creature class. It does not inherit any constructors from the Creature class; constructors are never inherited.

The Alien class also overrides the method speak in the Creature class to use its own version of this method implementing its own specific code to speak as an Alien object.

We also use the keyword super in the Alien constructor. The keyword super indicates that we are accessing a member of the direct super class. In this case we are calling the constructor of the Creature class using the keyword super in a direct subclass of Creature – Alien. The keyword super is explained in detail a little later in the chapter.

Note 

The constructor call of the base class Creature in the Alien class constructor using super(greeting) is not required to actually create the inherited data. If this was not called, the data in Creature would still exist, where by default the value of the variable greeting would merely be set to null (unassigned to a String object). Call the constructor of the super class to initialize the state of the data defined in the Creature class from the constructor of the Alien object that we are creating, where we are basically calling the constructor of the super class as if it were simply a method to initialize the data. It is a common practice in a subclass constructor to make a call to the super class constructor to initialize the state of variables defined in the super class. It is a very useful technique. We will look at the keyword super a little later in the chapter. Furthermore, a call to the super class constructor using super() can only be called first in the constructor before any other code; otherwise, the code will not compile.

Code Listing 4-6: Human.java

public class Human extends Creature { public Human(String greeting) { super(greeting); } public void speak() { System.out.println("Human says: " + getGreeting()); } }

The Human class is almost identical to the Alien class, except for its implementation of the speak method, which is specific to a Human object with the text "Human says: ", whereas an Alien object would have "Alien says: " before its greeting text. A Creature object would have "Creature says: " before its text greeting also, so we can distinguish which is which.

Code Listing 4-7: Beings.java

public class Beings { public static void main(String args[]) { Creature myCreature = new Creature("blub-blub, what the heck am I then"); Alien myAlien = new Alien("Dak-DakDakDak"); Human myHuman = new Human("Hello there"); myCreature.speak(); myAlien.speak(); myHuman.speak(); } }

In the main class Beings, create three objects. Each is an object of a different creature class. We have a Creature, an Alien, and a Human object. In the case of each, make a call to their respective speak methods. When you run this example, you should get output similar to the following figure.

Figure 4-6:

If this is a little confusing, added to the mix are any concerns about how the Object class is dealt with. Take a look at a hierarchical representation of the previous example in the following figure.

Figure 4-7:

In terms of a class that extends another class (or a class that is extended by another class), the relationship between them is known as being direct. So in our example, you would say that the Creature class is a direct super class of the Alien and Human classes, and that Object is a direct super class of the Beings and Creature classes. Equally, you would also say that the Alien and Human classes are direct subclasses of the Creature class and Beings and Creature are direct subclasses of the Object class. Therefore, the Object class is an indirect super class of the Alien and Human classes, and they are indirect subclasses of the Object class.

In conclusion, any class that does not extend another is a direct subclass of the Object class by default. A class that does extend another class is part of a hierarchy of inherited classes, which will always lead to the root Object class.

Inheritance Depending on Access Attribute

When we create an Alien object, an object of type Creature and an object of type Object that go into making the Alien object what it is also exist. The members that are inherited from the super class depend on the access attribute of this member when defined in the super class. For example, look at the relationship between the Creature class and its subclass Alien. The Alien class inherits the public members of its super class Creature, but it does not inherit the private member greeting of the Creature class. This does not mean that this variable does not exist; it simply means that this variable is inaccessible from an Alien class. If you go back to Figure 4-4, a diagram of access attributes, you can see the accessibility relationship between a class and one of its subclasses from within the same package and also from a separate package. These access attributes are true for inheritance as well. Based on the diagram, we can see that private members are never inherited by any subclass and members with no access attribute are not inherited if the super class is in a different package to the subclass.

Категории