Java多态(Polymorphism)


多态polymorphism 通过 分离what 和 how (即分离了“是什么”和“如何做”),从另一个角度分离接口和实现。多态可改善代码的组织结构和可读性,还能创建 可扩展程序–> 在项目初期和添加新功能时都可以“ 生长”的程序。

封装: 通过合并特征和行为来创建新的数据类型。Implementation hiding(“隐藏实现”通过将细节私有化而将接口和实现分离开)。而多态,则是消除类型之间的耦合关系

一、向上转型

skipped!

二、动态绑定

2.1 绑定

绑定是指: 将一个方法调用与一个“方法主体”关联起来。(as the original description: “Connecting a method call to a method body is called binding”)。说白了,就是确定是哪个对象(父还是子)来真正调用这个方法。

类似C++这样的语言,会在程序运行前就由编译器和连接器完成了绑定,这被称为“前期绑定”。
而Java采用的动态绑定,即在运行时根据对象的类型进行绑定。Java中除了static 和final方法(private方法是典型的 final方法),其余都是动态绑定。

这里我们理解了:为啥我们要将一个方法声明为final呢?一方面是防止其被覆盖,另一方面是因为能有效“关闭”动态绑定,告诉编译器不需要对其动态绑定,这样,编译器就能为final生成更有效的代码,不过,这点性能的提升,一般不值一提。

Polymorphism是提升代码拓展性的利器,用BruceEckel的话说:多态将“改变的事物”与“未变的事物”分离开的重要技术。

2.2 缺陷: 覆盖private方法

override 只针对非 private 方法,我们一般将子类中要override的方法名和父类的private方法名保持不一致。

public class PrivateOverride {

    /*由于 私有 f()方法和 子类的f()方法同名,不能构成override ,
    * 因此运行的仍然是 PrivateOverride#f() */
    private void f() {
        System.out.println("PrivateOverride.f");
    }

    public static void main(String[] args) {
        PrivateOverride derived = new Derived();
        derived.f();
    }
}

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

2.3 缺陷:域与静态方法

我们需要注意到:

  • override只针对非private的普通方法,而 域 和静态方法都是不能构成override的!在下例中,当Sub对象转型为Super引用时,任何域访问都是由编译器解析的,因此不是多态的!下例中,编译器为Super.field和Sub.field 分配了不同的存储空间,Sub实际上有两个被称为field的域:自有的和继承得到的。但是,在引用sub中的field时所产生的默认域并非Super版本的field域。因此为了得到Super.field,还必须显式指定super.field。
  • 静态方法的行为是不会构成多态性的
public class FieldAccess {

    public static void main(String[] args) {
        Super sup = new Sub();
        System.out.println(sup.field); //0
        System.out.println(sup.getField()); //1
        System.out.println("----------");

        Sub sub = new Sub();
        System.out.println(sub.field); //1
        System.out.println(sub.getField()); //1
        System.out.println(sub.getSuperField()); //0 

    }
}

class Super{
    public int field = 0;

    public int getField() {
        return field;
    }
}

class Sub extends  Super{
    public int field = 1;

    @Override
    public int getField() {
        return field;
    }

    public int getSuperField() {
        return super.field;
    }
}

三、构造器和多态

3.1 构造器调用链

构造器其实是static方法(隐式的),不具备多态性。
基类的构造器总是在导出类的构造过程中被调用,而且按照继承层次主键向上连接,以使每个基类的构造器都能被调用。

扫描二维码关注公众号,回复: 3766319 查看本文章

看下例中一个复杂类构造的顺序

  • 1、将SandWich中的的 b \c \ l 域初始化 为null
  • 2、调用基类的构造器: 即 meal --> lunch --> portableLunch --> Sandwich的构造器
  • 3、按声明顺序调成员的初始化方法 :即 Bread --> Cheese --> Lettuce的构造器
  • 4、调用导出类构造器 :即 Sandwich 的构造器

class Meal {
  Meal() { print("Meal()"); }
}

class Bread {
  Bread() { print("Bread()"); }
}

class Cheese {
  Cheese() { print("Cheese()"); }
}

class Lettuce {
  Lettuce() { print("Lettuce()"); }
}

class Lunch extends Meal {
  Lunch() { print("Lunch()"); }
}

class PortableLunch extends Lunch {
  PortableLunch() { print("PortableLunch()");}
}

public class Sandwich extends PortableLunch {
  private Bread b = new Bread();
  private Cheese c = new Cheese();
  private Lettuce l = new Lettuce();
  public Sandwich() { print("Sandwich()"); }
  public static void main(String[] args) {
    new Sandwich();
  }
} /* Output:
Meal()
Lunch()
PortableLunch()
Bread()
Cheese()
Lettuce()
Sandwich()
*///:~

3.2 构造器内部的多态方法的行为

可以参考参考3.1 中类初始化的说明,
需要特别指出的是:根据阿里的开发规约,我们应尽可能地让一个类简单初始化,禁止在类的构造器中调用其他方法。假如调用,也应该调用private或者final方法。

public class Glyph {
    
    // 甲
    void draw() {
        System.out.println("Glyph.draw");
    }

    public Glyph() {
        System.out.println(" Glyph before draw");
        // 在 构造器 中调用了override 方法
        draw();
        System.out.println(" Glyph after draw ");
    }
}

class RoundGlyph extends Glyph{
    private int radius = 1;

    public RoundGlyph(int radius) {
        this.radius = radius;
        System.out.println(" RoundGlyph. RoundGlyph() ,radius = " + radius);
    }

    // 乙
    void draw() {
        System.out.println(" RoundGlyph.draw + radius " + radius);
    }
}

class ConstructorTest{
    public static void main(String[] args) {
        Glyph roundGlyph = new RoundGlyph(10);
        }
}
/**
 * 输出:
 Glyph before draw
 RoundGlyph.draw + radius 0   (注意: 这里调用的不是 甲,而是 乙)
 Glyph after draw 
 RoundGlyph. RoundGlyph() ,radius = 10
 **/

四、协变返回类型

协变类型就是说导出类中覆盖基类的方法时可以返回基类中被override方法返回值类型的子类。

五、用继承来设计

继承容易被误用,当我们不确定使用组合还是继承时,使用组合多半能“保底”。使用组合模式,能让你的类层次结构更加灵活,同时组合可以动态选择类型,因此也就动态选择了行为

看下面这个简单的Demo:
Stage类是一个典型的组合模式,其内部持有了 Actor的引用作为域。
注意 "甲” 这一句:
change()方法将绑定的Actor类型实际给变换了,或者说,Stage对象的状态动态变化了,从而拥有了不同的行为。这其实就是一种简单的状态设计模式

假如用继承来实现Stage类,那将无法完成这种状态上的轻松变换了。

在这个简单的例子里:
我们得到一个准则:继承来表达行为的差异,用字段表达状态的变化。这个例子里:通过继承得到两个不同的类HappyActor \ SadActor ,而Stage通过组合使自身状态发生变化。


class Actor {
  public void act() {}
}

class HappyActor extends Actor {
  public void act() { print("HappyActor"); }
}

class SadActor extends Actor {
  public void act() { print("SadActor"); }
}

class Stage {
  private Actor actor = new HappyActor();
  // 甲
  public void change() { actor = new SadActor(); }
  public void performPlay() { actor.act(); }
}

public class Transmogrify {
  public static void main(String[] args) {
    Stage stage = new Stage();
    stage.performPlay();
    stage.change();
    stage.performPlay();
  }
} /* Output:
HappyActor
SadActor
*///:~

六、总结

skipped!

猜你喜欢

转载自blog.csdn.net/qq_30118563/article/details/83216904