Monday 7 January 2013

Chapter 2: Object Orientation; Overriding / Overloading

Overridden Methods

Any time you have a class that inherits a method from a superclass, you have the
opportunity to override the method (unless, as you learned earlier, the method is
marked final).
The key benefit of overriding is the ability to define behavior that's
specific to a particular subclass type. The following example demonstrates a Horse
subclass of Animal overriding the Animal version of the eat() method:

public class Animal
{
    public void eat()
    {
        System.out.println("Generic Animal Eating Generically");
    }
}

class Horse extends Animal
{
    public void eat()
    {
        System.out.println("Horse eating hay, oats, " + "and horse treats");
    }
}

For abstract methods you inherit from a superclass, you have no choice.
You must implement the method in the subclass unless the subclass is also abstract.
Abstract methods must be implemented by the concrete subclass, but this is a lot like saying
that the concrete subclass overrides the abstract methods of the superclass. So you
could think of abstract methods as methods you're forced to override.

public class TestAnimals
{
    public static void main(String[] args)
    {
        Animal a = new Animal();
        Animal b = new Horse(); // Animal ref, but a Horse object
        a.eat(); // Runs the Animal version of eat()
        b.eat(); // Runs the Horse version of eat()
    }
}

class Animal
{
    public void eat()
    {
        System.out.println("Generic Animal Eating Generically");
    }
}

class Horse extends Animal
{
    public void eat()
    {
        System.out.println("Horse eating hay, oats, " + "and horse treats");
    }

    public void buck()
    {
    }
}


Animal c = new Horse();
c.buck(); // Can't invoke buck();
// Animal class doesn't have that method

public class TestAnimals
{
    public static void main(String[] args)
    {
        Horse h = new Horse();
        h.eat(); // Not legal because Horse didn't inherit eat()
    }
}

class Animal
{
    private void eat()
    {
        System.out.println("Generic Animal Eating Generically");
    }
}

class Horse extends Animal
{
}

Invoking a Superclass Version of an Overridden Method

public class Animal
{
    public void eat()
    {
    }

    public void printYourself()
    {
        // Useful printing code goes here
    }
}

class Horse extends Animal
{
    public void printYourself()
    {
        // Take advantage of Animal code, then add some more
        super.printYourself(); // Invoke the superclass
        // (Animal) code
        // Then do Horse-specific
        // print work here
    }
}

class Animal {
public void eat() throws Exception {
// throws an Exception
}
}
class Dog2 extends Animal {
public void eat() { // no Exceptions }
public static void main(String [] args) {
Animal a = new Dog2();
Dog2 d = new Dog2();
d.eat(); // ok
a.eat(); // compiler error -
// unreported exception
}
}

This code will not compile because of the Exception declared on the
Animal eat() method. This happens even though, at runtime, the eat() method used
would be the Dog version, which does not declare the exception.

Overloaded Methods

Overloaded methods let you reuse the same method name in a class, but with
different arguments (and optionally, a different return type). Overloading a method
often means you're being a little nicer to those who call your methods, because your
code takes on the burden of coping with different argument types rather than forcing
the caller to do conversions prior to invoking your method.

The rules are simple:
- Overloaded methods MUST change the argument list.
- Overloaded methods CAN change the return type.
- Overloaded methods CAN change the access modifier.
- Overloaded methods CAN declare new or broader checked exceptions.

class Animal {
}

class Horse extends Animal {
}

class UseAnimals {
    public void doStuff(Animal a) {
        System.out.println("In the Animal version");
    }

    public void doStuff(Horse h) {
        System.out.println("In the Horse version");
    }

    public static void main(String[] args) {
        UseAnimals ua = new UseAnimals();
        Animal animalObj = new Animal();
        Horse horseObj = new Horse();
        ua.doStuff(animalObj);
        ua.doStuff(horseObj);
    }
}

The output is what you expect:
in the Animal version
in the Horse version

Polymorphism in Overloaded and Overridden Methods

public class Animal {
    public void eat() {
        System.out.println("Generic Animal Eating Generically");
    }
}

public class Horse extends Animal {
    public void eat() {
        System.out.println("Horse eating hay ");
    }

    public void eat(String s) {
        System.out.println("Horse eating " + s);
    }
}
Notice that the Horse class has both overloaded and overridden the eat()
method. Table 2-2 shows which version of the three eat() methods will run
depending on how they are invoked.


Don’t be fooled by a method that’s overloaded but not overridden by a
subclass. It’s perfectly legal to do the following:
public class Foo {
void doStuff() { }
}
class Bar extends Foo {
void doStuff(String s) { }
}
The Bar class has two doStuff() methods: the no-arg version it inherits
from Foo (and does not override), and the overloaded doStuff(String s) defined in the
Bar class. Code with a reference to a Foo can invoke only the no-arg version, but code
with a reference to a Bar can invoke either of the overloaded versions.

No comments:

Post a Comment