多态(Polymorphism)


Polymorphic method invocations apply only to instance methods. You can always refer to an object with a more general reference variable type (a superclass or interface), but at runtime, the ONLY things that are dynamically selected based on the actual object (rather than the reference type) are instance methods. Not static methods. Not variables.Only overridden instance methods are dynamically invoked based on the real object's type.


1. Overridden Methods

(1) Example of using polymorphism looks like this:

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() { }
}

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()
//      b.buck();               //Can't invoke buck();
                                //Animal class doesn't have that method  
    }
}

b is an Animal reference, so at compile time, the compiler knowsb.eat()is OK becauseAnimal has eat() method. Butb actually is aHorse object, so the JVM dynamically calls theHorse eat()method at runtime.



(2) Rules for overriding a method

The overriding method cannot have a more restrictive access modifier than the method being overridden (for example, you can't override a method marked public and make it protected).

Let's modify the polymorphic example we saw earlier in this section:

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

class Horse extends Animal {
    private void eat() {        // whoa! - it's private!                                 
        System.out.println("Horse eating hay, oats, " + "and horse treats");
    }
    public void buck() { }
}

public class TestAnimals2 {
    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()
    }
}

This code does not compile:



If this code compiled (which it doesn't), the following would fail at runtime:

Animal b = new Horse(); // Animal ref, but a Horse
b.eat();                // object , so far so good
                        // Meltdown at runtime!

The rules for overriding a method are as follows:

(1)The argument list must exactly match that of the overridden method. If they don't match, you can end up with an overloaded method you didn't intend.

(2)The return type must be the same as, or a subtype of, the return type declared in the original overridden method in the superclass.(covariant returns)

(3)The access level can't be more restrictive than the overridden method's.(But CAN be less restrictive)

4)The overriding method CAN throw any unchecked (runtime) exception, regardless of whether the overridden method declares the exception. 

(5)The overriding method must NOT throw checked exceptions that are new or broader than those declared by the overridden method. For example, a method that declares a FileNotFoundException cannot be overridden by a method that declares a SQLException, Exception, or any other non-runtime exception unless it's a subclass of FileNotFoundException.

(6)The overriding method can throw narrower or fewer or no exceptions.

(7)You cannot override a method marked final or static.

(8) static methods can't be overridden! But they can be redefined in a subclass.  


2. Overloaded Methods

The rules are simple:

(1)Overloaded methods MUST change the argument list.
(2)Overloaded methods CAN change the return type.
(3)Overloaded methods CAN change the access modifier.
(4)Overloaded methods CAN declare new or broader checked exceptions.

(5)A method can be overloaded in the same class or in a subclass.


3. Differences Between Overloaded and Overridden Methods


4. Some examples

(1)Pitfall:“overriding” private methods

public class PrivateOverride {
  private void f() { System.out.println("private f()"); }
  public static void main(String[] args) {
    PrivateOverride po = new Derived();
    po.f();
  }
}

class Derived extends PrivateOverride {
  public void f() { System.out.println("public f()"); }
}

output:



(2) Behavior of polymorphic methods inside constructors

public class CallInsideConstructor {                                                        
    public CallInsideConstructor() {
        f1();
    }

    public void f1() {
        System.out.println("inside f1()");
    }

    public static void main(String[] args) {
        new CallInsideConstructor();
    }
}

output:



The following code is strange:

class Glyph {                                 
    void draw() { System.out.println("Glyph.draw()"); }
    Glyph() {
        System.out.println("Glyph() before draw()");
        draw();               //it calls the overriding method in subclass RoundGlyph
        System.out.println("Glyph() after draw()");
    }
}

class RoundGlyph extends Glyph {
    private int radius = 1;
    RoundGlyph(int r) {
        radius = r;
        System.out.println("RoundGlyph.RoundGlyph(), radius = " + radius);
    }
    void draw() {
        System.out.println("RoundGlyph.draw(), radius = " + radius);
    }
}

public class PolyConstructors {
    public static void main(String[] args) {
        new RoundGlyph(5);
  }
}

output:



If you call a dynamically bound method inside a constructor, the overridden definition for that method is also used. 

However, the effect of this call can be rather unexpected because the overridden method will be called before the object is fully constructed.

Constructor "initializes" the state of the object and is not used for "creating" the object as the name deceptively suggests. When JVM loads a class, its entire definition is injected into the memory.When a draw() method is called in the superclass constructor, the runtime looks up the polymorphic definition of thedraw()method and finds that there is an overridden draw() method available and thus calls the subclass(RoundGlyph) draw() implementation. The point to note that the entire class definition is always available in memory and calling an object method has no connection to whether the object is fully initialized or not. 



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值