2.38k likes | 2.52k Views
Java Unit 14 The Methods toString (), equals(), and clone(). 14.1 toString () 14.2 equals() 14.3 What is Cloning? 14.4 Handling and Throwing Exceptions Outline continued on next overhead. 14.5 The clone() Method in the Object Class and the Cloneable Interface
E N D
14.1 toString() • 14.2 equals() • 14.3 What is Cloning? • 14.4 Handling and Throwing Exceptions • Outline continued on next overhead
14.5 The clone() Method in the Object Class and the Cloneable Interface • 14.6 Overriding the clone() Method for Objects without References • 14.7 Overriding the clone() Method for Objects with a Reference • 14.8 The Importance of Cloning
A Preliminary Blip • This is in answer to a random student question. • I will never use this and you will not be expected to use it or tested on it • The question: • Is it possible to call one constructor from inside another? • The answer is yes. • An example showing the syntax is given on the following overhead.
public class FoodVX • { • private String name; • private double wholesaleCost; • private double markup; • public FoodV3(String nameIn) • { • name = nameIn; • } • public FoodV3(String nameIn, double wholesaleCostIn, double markupIn) • { • this(nameIn); • wholesaleCost = wholesaleCostIn; • markup = markupIn; • } • …
14.1.1 Introduction • Programmer written classes inherit three important methods from the Object class, toString(), equals(), and clone(). • Whether to override them and how to do so correctly are the topics of this unit. • A progression of things will be shown and the food hierarchy class designs will change piece by piece.
The version number for the food hierarchy will remain V6 for the whole unit. • At the end of the unit, V6 will refer to the classes containing the preferred implementations of the topics covered in the unit.
14.1.2 For the Purposes of the Discussions in this Unit, Assume that FoodV6 is Not Abstract • For the purposes of illustrating the topics of this unit it's useful to have a concrete superclassand subclass. • That way instances of each can be constructed and methods can be called on them. • At the end of the last unit, the final version of the Food class was abstract. • In this unit the Food class will revert to being concrete.
If the FoodV6 class were to remain abstract, it would still be possible to write a toString(), equals(), or clone() method for it. • The methods would still be useful because they could be called from a subclass by means of the keyword super. • However, it would not be possible to construct a FoodV6 object and call these methods on it.
14.1.3 toString() is Overridden in System Supplied Classes • The toString() method returns a string containing information about an object. • The method is overridden in system supplied classes and it is usually desirable to do the same in a programmer written class. • On the following overhead code is shown where the toString() method is called on a system supplied object:
Point myPoint = new Point(10, 20); • String myPointString = myPoint.toString(); • myterminal.println(myPointString);
For a class where the method has been overridden this is what you get: • The name of the class followed by square brackets containing the names and values of the instance variables of the object. • java.awt.Point[x=10,y=20]
14.1.4 The Inherited Version of toString() is not very Useful • The version of the method inherited from the Object class does not produce very useful output. • On the following overhead code is shown where the toString() method is called on an object where the method has not been overridden:
FoodV6 myFood = new FoodV6("Ace", "Peas", "grams"); • String myFoodString = myFood.toString(); • myterminal.println(myFoodString);
For a class where the method has not been overridden this is what you get: • The name of the class followed by an @ sign, followed by digits and letters. • FoodV6@720eeb
The digits and letters form a hexadecimal integer which is referred to as a hash code. • The hash code is a system supplied, unique identifier for a given object during any program run. • The hash code value may differ from run to run.
It is worth recalling that a Java program does not have direct access to memory. • It only has references to objects, and the system takes care of their location in memory. • The location of an object in memory will not change during the course of a program run. • This means that the system may derive a hash code from an object’s address
14.1.5 The Method println() Depends on toString() • It is possible to get the same output without expressly calling toString(). • Consider the following code: • Point myPoint= new Point(10, 20); • myterminal.println(myPoint); • FoodV6 myFood= new FoodV6("Ace", "Peas", "grams"); • myterminal.println(myFood);
Here are the results: • java.awt.Point[x=10,y=20] • FoodV6@720eeb • This happens because the implementation of the method println() depends on toString(). • println() internally calls toString() when printing an object, whether toString() has been overridden or not.
14.1.6 String Concatenation Depends on toString() • The toString() method is also used by the system to support the concatenation of strings and objects. • The following is valid code: • String myString; • myString= “xyz”; • myString= myString+ myFood; • The result of this code is the concatenation of the contents of myStringand whatever is returned by a call to toString() on the reference myFood.
14.1.7 It is Desirable to Override toString() • There are several things to be gleaned from these examples. • It is more useful for toString() to return the name of the class followed by instance variables and their values rather than a hash code. • Even if you don’t call toString()directly, its implementation is important because of its use by println() and in concatenation. • This means that every programmer written class should override the toString()method.
Several more observations can be made about toString() that relate to how it should be implemented. • Notice that even though Point is a subclass in a hierarchy, its parent classes are not shown as part of the return value from toString(). • For a system supplied class its package is shown, but this will not be an issue for us since we are not storing our classes in packages.
14.1.8 Example Code for Implementing toString() • Here is a simple, complete implementation of the toString() method for the FoodV6 class. • public String toString() • { • String returnString = • "FoodV6[brand=" + brand • + ", name=" + name • + ", units=" + units + "]"; • return returnString; • }
14.1.9 The Implementation of toString() in Subclasses • You can write the same simple kind of toString() method for a subclass. • Notice that if you do this you will have to call get methods in order to retrieve the values of inherited instance variables. • On the following overhead a simple implementation of toString() is shown for the PackagedFoodV6 class.
public String toString() • { • String returnString = • "PackagedFoodV6[brand=" + getBrand() • + ", name=" + getProductName() • + ", units=" + getUnits() • + ", size=" + size • + ", itemCost=" + itemCost + "]"; • return returnString; • }
14.1.10 The getClass() Method and the getName() Method • Recall the instanceof operator. • If you already had a class in mind, using instanceof, it was possible to test whether an object reference was to an instance of that class. • Java has an even more flexible feature. • Given an object reference, you can find out what class it’s of without having an idea in advance. • This can be useful in writing a general toString() method.
Objects of any class inherit the getClass() method from the Object class. • If you call getClass() on an object, the return value is the instance of the Classclass for the class of the object. • The Class class also contains a method named getName(). • If you call getName() on a Class object, the name of that class will be returned as a String.
The getClass() method along with the getName() method can be used when writing a toString() method. • Other uses for getClass() will also be given later on in this unit.
Here is a snippet of the API documentation for the getClass() method. • getClass • public final ClassgetClass() Returns the runtime class of an object.
14.1.11 Using getClass(), getName(), and super in toString() • getClass() and getName() can be used when writing a toString() method. • The toString() method in the FoodV6 class could be rewritten as shown on the next overhead. • It may seem a little elaborate to rely on the system to give the class name rather than hardcoding, but the benefit will become apparent when considering how to write a toString() method for a subclass.
public String toString() • { • String returnString = • getClass().getName() • + [brand=" + brand • + ", name=" + name • + ", units=" units + "]"; • return returnString; • }
If the toString() method for FoodV6 is written as shown above, then it is possible to write the toString() method for its subclass, PackagedFoodV6 as shown below. • public String toString() • { • String returnString = • super.toString() • + " [size=" + size • + ", itemCost=" + itemCost + "]"; • return returnString; • }
The call to toString() in the subclass would give output with two pairs of square brackets. • The first pair would contain the instance variables inherited from the superclass. • The other pair would contain the instance variables defined locally in the subclass. • In other words, this version of toString() would return something of this form. • PackagedFoodV6[superclass instance variables][subclass instance variables]
Recall that this is an implementation in a subclass • The implicit parameter in the call to super would be a PackagedFoodV6 object • Dynamic binding means that the call to getClass().getName() in the superclasstoString() method at runtime would produce a result of PackagedFoodV6, not FoodV6. • Thus, the output would still be correct for the subclass, showing the subclass name, not the superclass name.
14.1.12 How Not to Use super in toString() • Suppose in the superclass you hardcoded the superclass name in the toString() method • You didn’t use getClass().getName() • Suppose in the subclass toString() method you first called super.toString() • Then you hardcoded the subclass name in the subclass toString() method • Then you took care of the subclass instance variables and their values
The return value for the subclass toString() method would show full inheritance information. • The output would take the form shown below. • SuperClass[superclass instance variables]SubClass[subclass instance variables] • There is nothing wrong with this, but it’s not recommended because this not the expected output of the toString() method. • From a practical point of view, if the inheritance hierarchy is deep, the output would be excessively long.
14.2.1 The Inherited equals() Method Tests for Equality of Reference • You have encountered the equals() method when doing comparisons for ifs and loops. • The equals() method is provided in the Object class. • It implements a test of equality of reference and is inherited by programmer written classes. • It is overridden in system supplied classes by an implementation that tests equality of contents.
The equals() method in the Object class works by making use of the hash codes mentioned in the previous section. • The hash code is an object’s unique, system supplied identifier, which does not change during the course of a program run. • The system tests for equality of reference by testing for equality of hash codes. • If the hash codes are the same, then the references must be to the same object.
14.2.2 Overriding the equals() Method to Test for Equality of Contents • The goal now is to override the equals() method in the FoodV6 class so that it tests equality of contents, not equality of reference, for programmer written classes. • With a certain amount of knowledge about inheritance, this is not hard to do. • In general when overriding a method, the return type of the method, the method name, and the parameter list of the new implementation have to be the same as those in the original.
In the API documentation you will find the following information for the equals() method: • public boolean equals(Object obj)
This means that with the exception of the name of the formal parameter, obj, which can be changed, the declaration in the class where this is being overridden has to be the same. • In the method declaration shown below, the name of the explicit parameter has no effect, but it indicates the case we want to deal with: • public boolean equals(Object anotherFood)
The implicit parameter of a call to this method will be a FoodV6 object. • In order for equality to be possible, the explicit parameter should also be a reference to a FoodV6. • However, the formal parameter has to be typed as an Object reference to agree with the method we’re overriding.
In spite of its name, anotherFood,the formal parameter will be an Object reference. • If the actual parameter passed during a call is a FoodV6 reference, then the formal parameter anotherFood will be a superclass reference. • As pointed out in the previous unit, superclass references to subclass objects are OK. • It is possible to recover subclass references from superclass references.
14.2.3 First Check that Objects are of the Same Class; Then Check the Contents • From a theoretical point of view, equality of objects only makes sense if the objects being compared are of the same class. • Because the explicit parameter is typed Object, and the Object class is at the top of the inheritance hierarchy, there is nothing syntactically preventing a user from making a call with an explicit parameter of any class. • Therefore, the first task in writing an equals() method will be to check the type of the explicit parameter.
There is also a practical consideration. • In order to continue and test the contents of the objects for equality, it will be necessary to cast the explicit parameter superclass reference to a subclass reference. • This is because the superclass reference is distinctly limited. • In order to do the comparisons between the instance variable values of the implicit and explicit parameters, you will need access to the values of the instance variables of the actual parameter.
This will only be possible if you recover the subclass reference. • Remember that you need to protect yourself from incorrectly trying to cast the explicit parameter back to a reference type that it never was. • A subclass reference can be recovered by casting, but a run time error results if a cast is done on a reference that is not of that class.
If you protect the cast and successfully cast the explicit parameter to the type of the implicit parameter—then you can compare their contents • Therefore, a correct implementation of the equals() method will start by testing whether or not the explicit parameter is of the right class.
In summary, an implementation of equals() involves two things. • 1. Checking to make sure that the explicit parameter is of the right type and recovering the subclass reference. • 2. Then testing for equality of the instance variables. • On the next overhead a simple, complete implementation of the equals() method for the FoodV6 class is shown.