7.
3 Inheritance and Methods
All classes inherit from Object, objects of
any type will always have an Object part and
so they can all use the toString method
defined there. In the same way, all classes
can use the equals method defined in the
Object class.
The default toString method defined in the
Object class returned a string that contained
the class name and a memory address of the
current object of the class.
(System.out.println(f) would produce the
output: Fraction@1cc7c5).
To make the toString method more useful,
we override the default definition in the
Object class with one that had the same
signature but whose functionality was more
appropriate.
A Fraction Object
When a call is made to an instance method,
Java looks for a method with the appropriate
signature starting from the lowest class in
the hierarchy of the object.
Example 1
If we were to write
Fraction f = new Fraction (2,3);
Then the call f.toString( ) would invoke the
toString method of the Fraction class, not
the version in the Object class. If we had not
written a toString method for the Fraction
class, then Java would have started working
its way up the class hierarchy looking for a
method with the appropriate signature. In
this case, the next class up the hierarchy is
Object where the default version of toString
resides.
Suppose in our Object – Person – Student
hierarchy, we have written equals methods
for the class Person and Student, and we
have constructed objects with the following
statements:
Object o = new Student ();
Person p = new Student ();
Now, o.equals(p), Java uses the object’s
type rather than the variable’s type to
determine which method to call. Java also
looks for a method with the appropriate
signature starting from the lowest class in
the hierarchy of the object. In this case, there
is an equals method in the Student class so
that is the one that Java uses.
We define a method with the header
public void foo ()
in the Student class. If we now write
Person p = new Student ();
p.foo();
the compiler produces an error. The
compiler does not look at previous lines of
code so it is not sure what type p will have
at execution time. We solve this problem by
casting.
Person p = new Student ();
((Student)p).foo();
Then the fragment will compile and execute
correctly. (method call has a higher
precedence than a cast and we need
parentheses).