多态

,运行根据对象类型进行绑定=“后期绑定”。

上面这段话先放一边,看起来有点专业化看不太懂,下面先放上一段代码:

class Instrument {
  public void play(Note n) {
    print("Instrument.play()");
  }
}
class Stringed extends Instrument {
  public void play(Note n) {
    print("Stringed.play() " + n);
  }
}

class Brass extends Instrument {
  public void play(Note n) {
    print("Brass.play() " + n);
  }
}
public class Wind extends Instrument {
  // Redefine interface method:
  public void play(Note n) {
    System.out.println("Wind.play() " + n);
  }
} ///:~

public class Music2 {
  public static void tune(Wind i) {
    i.play(Note.MIDDLE_C);
  }
  public static void tune(Stringed i) {
    i.play(Note.MIDDLE_C);
  }
  public static void tune(Brass i) {
    i.play(Note.MIDDLE_C);
  }
  public static void main(String[] args) {
    Wind flute = new Wind();
    Stringed violin = new Stringed();
    Brass frenchHorn = new Brass();
    tune(flute); // No upcasting
    tune(violin);
    tune(frenchHorn);
  }
} 

输出结果:

Wind.play() MIDDLE_C
Stringed.play() MIDDLE_C
Brass.play() MIDDLE_C

Wind,Stringed,Brass都继承与Instrument ,这种写法更清晰能看到Wind传入Wind并使用自己的方法更容易符合我们人的思想对不对,但是这样写现在是3个导出类(继承Instrument 这三个类),那万一上百个,是不是要写上百个tune()方法是不是不太合理。

那现在我们把改成这样

 public static void tune(Instrument i) {
    i.play(Note.MIDDLE_C);
  }

Wind,Stringed,Brass三个类型的参数是不是可以传到上面的这个方法,就不需要写三个方法了。但是这样的话,编译器又不是我们人脑,它怎么知道你传这个参数我到底给你调用那个方法。这就涉及到方法调用绑定。

程序运行前绑定=“前期绑定”这是默认方式,而想解决上面我们那种疑问就需要到“后期绑定”又叫运行时绑定,动态绑定。

运行根据对象类型进行绑定=“后期绑定”

public class Shape {
  public void draw() {}
  public void erase() {}
} ///:~

public class Circle extends Shape {
  public void draw() { print("Circle.draw()"); }
  public void erase() { print("Circle.erase()"); }
} ///:~
public class Square extends Shape {
  public void draw() { print("Square.draw()"); }
  public void erase() { print("Square.erase()"); }
} ///:~
public class Triangle extends Shape {
  public void draw() { print("Triangle.draw()"); }
  public void erase() { print("Triangle.erase()"); }
} ///:~

public class RandomShapeGenerator {
  private Random rand = new Random(47);
  public Shape next() {
    switch(rand.nextInt(3)) {
      default:
      case 0: return new Circle();
      case 1: return new Square();
      case 2: return new Triangle();
    }
  }
} ///:~
public class Shapes {
  private static RandomShapeGenerator gen =
    new RandomShapeGenerator();
  public static void main(String[] args) {
    Shape[] s = new Shape[9];
    // Fill up the array with shapes:
    for(int i = 0; i < s.length; i++)
      s[i] = gen.next();
    // Make polymorphic method calls:
    for(Shape shp : s)
      shp.draw();
  }
} /* Output:
Triangle.draw()
Triangle.draw()
Square.draw()
Triangle.draw()
Square.draw()
Triangle.draw()
Square.draw()
Triangle.draw()
Circle.draw()
*///:~

像上面这样写RandomShapeGenerator 就是一个工厂分配给你是Circle、Triangle还是Square,这就是工厂模式。

而return处就发生了向上转型,return返回之后就是Sharp s = new Circle/Triangle/Square,而产生向上转型,那么根据后期绑定,就可以执行到不同的draw()方法,也就没了我刚开始那样的Circle/Triangle/Square的draw()方法我要三个,这样就省去一部分的代码对不对。(其实这里我还是没理解后期绑定底层是如何运作的,书中也没写,后续看到我会补充)

而到这里可能大家还是一知半解,就省去一部分代码,多态到底好在哪。

将上面Music2代码改动下

public class Music2 {
  public static void tune(Instrument i) {
    i.play(Note.MIDDLE_C);
  }
 public static void tuneAll(Instrument[] e) {
    for(Instrument i : e)
      tune(i);
  }	
 
  public static void main(String[] args) {
    Wind flute = new Wind();
    Stringed violin = new Stringed();
    Brass frenchHorn = new Brass();
    tune(flute); // No upcasting
    tune(violin);
    tune(frenchHorn);
  }

乍一看好像没什么,不就是缩减成一个方法了吗。但是看到里面tuneALL()里面用到    for(Instrument i : e) tune(i) 这里Instrument是不是父类类型,所以可以适用于Wind/Stringed/Percussion三种类型,而如果没有后期绑定也就是多态,上面这个方法按照我们最开始那样是不是要写3个tune()方法外加3个for(Wind/Stringed/Percussion i : e) tune(i)加起来6个方法,而且假设我又增加一个类型继承Instrument,我还要用到tuneALL(),我是不是又要改动代码,费时费力又不讨好。那有了多态,你属于Instrument类型,根据后期绑定帮你找到你对应的方法执行,是不是又省去代码,又不用修改代码,不会影响到你其他的代码上!而这才是多态的关键之处。

但是多态还是有缺陷,缺陷一、非private修饰的方法才能被重载。

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

class Derived extends PrivateOverride {
  public void f() { print("public f()"); }
} /* Output:
private f()
*///:~

按照这个逻辑在我们的个人想法中,应该输出public f(),但是private修饰的发f()没办法在Derived 中重载,后期绑定无法找到对应的Derived 的方法执行,所以只有输出基类(PrivateOverride )中的发f()方法。

 缺陷一、域和静态方法。

运行根据对象类型进行绑定=“后期绑定”,那这两个都是在运行前做好的,肯定没办法多态咯。

构造器与多态(刚开始我的疑问,怎么突然冒出来个构造器,就是因为多态是根据运行时对象来的,构造器可以当做是static来看的,所以在多态之前,我们有必要了解下构造器):

一、顺序(看起来有点懵,其实也是跟之前构造器说的那块,先最底层然后变量定义,到构造器,没啥区别)

二、继承和清理(这里就举个例子,在构造一个对象时其实就是从最底层到最外层对吧,那举个例子,我们穿衣服就是先穿内衣到外套,那我们脱下来也是先外套再内衣对吧,所以在IO的时候我们关闭流,其实也就是这个道理)

垃圾回收让我们省去了释放对象,而当我们要有一些需求时,导出类(继承别的类)重写了基类(被继承类)的清理方法,记得需要super中的清理方法,不然的话,基类就不会清理,这点需要特殊注意。而在此基础上,我们可以根据多态提高的好处,在清理时设置static修饰变量,因为static上面说了是跟类级别,而多态是根据对象运行时,那就可以用来计数防止溢出(这个看起来有点难懂配合书中的代码看吧,)。

三、构造器内部的多态方法的行为(这里根据下面代码解释下,刚开始我也有点懵。我们知道多态是在运行根据对象类型进行绑定,那当你启动下面这个程序时,按照我们之前说的,那就是先执行基类的变量定义,到构造器,这时候还没有执行到main里面的new RoundGlyph(5),没有触发多态,所以这时候编译器会给变量先赋值二进制的0,对象就是null。所以我们正常的理解应该只有输出RoundGlyph.RoundGlyph(), radius = 5,但是多了一句RoundGlyph.draw(), radius = 0,理解这个可以对我们有时候有些莫名其妙的问题有一点危机意识)

class Glyph {
  void draw() { print("Glyph.draw()"); }
  Glyph() {
    print("Glyph() before draw()");
    draw();
    print("Glyph() after draw()");
  }
}	

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

public class PolyConstructors {
  public static void main(String[] args) {
    new RoundGlyph(5);
  }
} /* Output:
Glyph() before draw()
RoundGlyph.draw(), radius = 0
Glyph() after draw()
RoundGlyph.RoundGlyph(), radius = 5
*///:~

协议返回类型WheatMill重写process方法返回值Wheat类型继承Grain所以可以这样写,也是重写,java5之前不行。

基类与导出类拥有完全相同的接口方法称为纯继承,而继承往往是导出类“is-like-a”像一个基类,是基类的扩展,导出类有基类的所有非private方法,我们在上面可以知道导出类是可以向上转型,而向下转型则需要强转并且转型成功才可以调用导出类的方法,否则就报错ClassCastExceotion。

 

最后总结(再次偷懒):

.

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值