[   ^ to index...   |   <-- previous   |   next -->   ]

Example, ct'd

Recall from lecture that a reference may refer to any instance of a class, or any instance of its subclasses. Hence, it is always legal to assign "up" the heirarchy---a subclass instance may be assigned to a superclass reference.

Given the subclassing relationships we deduced on the previous page, which of the following lines of code should be legal, based on the static types of the references?

    FruitPlant fp = new FruitPlant();           //  1
    ApplePlant ap = new ApplePlant();           //  2

    FruitPlant fp2 = ap;                        //  3
    ApplePlant ap2 = fp;                        //  4
    ApplePlant ap3 = fp2;                       //  5

    FruitEatingFly ffly = new FruitEatingFly(); //  6
    ffly.eat(fp.produce());                     //  7
    ffly.eat(ap.produce());                     //  8

    AppleEatingFly afly = new AppleEatingFly(); //  9
    afly.eat(fp.produce());                     // 10
    afly.eat(ap.produce());                     // 11

Java overriding rules

The "natural" subtyping rule that I have explained so far states: if I have a method in a class A, then a class B may be a subtype if it has methods with return types at least as specific, and parameter types at least as general. If class B wishes to override a method in A, it may define an overriding method with more general argument types and more specific result types.

Java uses a more restricted version of this "natural" overriding rule. In Java, if a subclass wishes to override a method in its superclass, it must define a method such that:

  1. The method's return type exactly the same as the superclass method's return type.

  2. The method's name and parameter types are exactly the same as the superclass method's parameters.

Notice that the types are invariant. Therefore, we could not use directly the Plant or Fly classes on the previous page. The best we could do for FruitEatingFly would be this version:

    class AppleEatingFly
    {   void eat(Apple a) { ... }
    }
    class FruitEatingFly extends AppleEatingFly
    {   void eat(Apple a) { ... }
        void eat(Fruit f) { ... }
    }

(Methods cannot be overloaded on return type; how does this affect our plant example?)


Keunwoo Lee
Last modified: Wed May 30 23:13:16 PDT 2001