多形性

来源:互联网 时间:1970-01-01

虽然是第二次看,感觉这部分还是要仔细看。多形性是面向对象编程语言的第三中特性。

首先是上溯造型(这个是什么意思就不说了),为什么需要上溯造型呢?很多书上都会用“画图”或者“乐器”的程序来和大家解释:

class Note{

private int value;

private Note(int val) { value = val; }

public static final Note

middleC = new Note(0),

cSharp = new Note(1),

cFlat = new Note(2);} // Etc.
class Instrument {
public void play(Note n) {
System.out.println("Instrument .play()");
}
}
class Wind extends Instrument {
public void play(Note n) {
System.out.println("Wind .play()");
}
}
class Stringed extends Instrument {
public void play(Note n) {
System.out.println("Stringed .play()");
}
}
class Brass extends Instrument {
public void play(Note n) {
System.out.println("Brass .play()");
}
}
public class test {
public static void tune(Wind i) {
i.play(Note .middleC);
}
public static void tune(Stringed i) {
i.play(Note .middleC);
}
public static void tune(Brass i) {
i.play(Note .middleC);
}
public static void main(String[] args) {
Wind flute = new Wind ();
Stringed violin = new Stringed ();
Brass frenchHorn = new Brass ();
tune(flute);
tune(violin);
tune(frenchHorn);
}
}
输出结果:
Wind.play()
Stringed.play()
Brass.play()

神奇吧,但是程序是怎么知道的?显然在编译的时候确定是不可能的。所以这只能“运行期绑定”了。那“运行期绑定”是怎么实现的?一般认为是在对象中安插关于类型的特殊信息(细节我还不清楚)。有了这个显然会给我们带来极大的方便,因为只要你是乐器,我调用演奏的方法你就能正确地发出声音。

继承不断地进化,就出现了“抽象类”,只要用“abstract”来修饰一个类就可以了(如果有抽象方法的话必须为抽象类):

 1 import java.util.*;
2 abstract class Instrument {
3 int i;
4 public abstract void play();
5 public String what() {
6 return "Instrument";
7 }
8 public abstract void adjust();
9 }
10 class Wind extends Instrument {
11 public void play() {
12 System.out.println("Wind.play()");
13 }
14 public String what() { return "Wind"; }
15 public void adjust() {}
16 }
17 class Percussion extends Instrument {
18 public void play() {
19 System.out.println("Percussion.play()");
20 }
21 public String what() { return "Percussion"; }
22 public void adjust() {}
23 }
24 class Stringed extends Instrument {
25 public void play() {
26 System.out.println("Stringed.play()");
27 }
28 public String what() { return "Stringed"; }
29 public void adjust() {}
30 }
31 class Brass extends Wind {
32 public void play() {
33 System.out.println("Brass.play()");
34 }
35 public void adjust() {
36 System.out.println("Brass.adjust()");
37 }
38 }
39 class Woodwind extends Wind {
40 public void play() {
41 System.out.println("Woodwind.play()");
42 }
43 public String what() { return "Woodwind"; }
44 }
45 public class test {
46 static void tune(Instrument i) {
47 i.play();
48 }
49 static void tuneAll(Instrument[] e) {
50 for(int i = 0; i < e.length; i++)
51 tune(e[i]);
52 }
53 public static void main(String[] args) {
54 Instrument[] orchestra = new Instrument[5];
55 int i = 0;
56 orchestra[i++] = new Wind();
57 orchestra[i++] = new Percussion();
58 orchestra[i++] = new Stringed();
59 orchestra[i++] = new Brass();
60 orchestra[i++] = new Woodwind();
61 tuneAll(orchestra);
62 }
63 }

自从有了抽象类,就不能随随便便地申请对象了,还得乖乖地实现抽象方法。既然已经到这个地步了,那和不走的更远?

 1 import java.util.*;
2 interface CanFight {
3 void fight();
4 }
5 interface CanSwim {
6 void swim();
7 }
8 interface CanFly {
9 void fly();
10 }
11 class ActionCharacter {
12 public void fight() {}
13 }
14 class Hero extends ActionCharacter implements CanFight, CanSwim, CanFly {
15 public void swim() {}
16 public void fly() {}
17 }
18 public class test {
19 static void t(CanFight x) { x.fight(); }
20 static void u(CanSwim x) { x.swim(); }
21 static void v(CanFly x) { x.fly(); }
22 static void w(ActionCharacter x) { x.fight(); }
23 public static void main(String[] args) {
24 Hero i = new Hero();
25 t(i); // Treat it as a CanFight
26 u(i); // Treat it as a CanSwim
27 v(i); // Treat it as a CanFly
28 w(i); // Treat it as an ActionCharacter
29 }
30 }

接口,貌似是抽象类的进化,但是貌似没那么简单。可以“implements”多个接口,“interface”里面的东西全部被认为是“static&final”的(这这点很容易理解)。而且对接口更好的理解是“协议”。实现了多个接口的类可以上溯为多个类型(这个很好理解哈)。这个地方有个“static&final”引起的小插曲,应该算是Java怎么实现“enum”的吧:

 1 public final class test {
2 private String name;
3 private test(String nm) { name = nm; }
4 public String toString() { return name; }
5 public final static test
6 JAN = new test("January"),
7 FEB = new test("February"),
8 MAR = new test("March"),
9 APR = new test("April"),
10 MAY = new test("May"),
11 JUN = new test("June"),
12 JUL = new test("July"),
13 AUG = new test("August"),
14 SEP = new test("September"),
15 OCT = new test("October"),
16 NOV = new test("November"),
17 DEC = new test("December");
18 public final static test[] month = {
19 JAN, JAN, FEB, MAR, APR, MAY, JUN,
20 JUL, AUG, SEP, OCT, NOV, DEC
21 };
22 public static void main(String[] args) {
23 test m = test.JAN;
24 System.out.println(m);
25 m = test.month[12];
26 System.out.println(m);
27 System.out.println(m == test.DEC);
28 System.out.println(m.equals(test.DEC));
29 }
30 }

有值有类型安全检查,还算不错吧?不过第一次见到这个方法好像不是在这本书上。

关于继承方面的就到此为止,下面开始“内部类”:

 1 public class test {
2 class Contents {
3 private int i = 11;
4 public int value() { return i; }
5 }
6 class Destination {
7 private String label;
8 Destination(String whereTo) {
9 label = whereTo;
10 }
11 String readLabel() { return label; }
12 }
13 public void ship(String dest) {
14 Contents c = new Contents();
15 Destination d = new Destination(dest);
16 }
17 public static void main(String[] args) {
18 test p = new test();
19 p.ship("Tanzania");
20 }
21 }

看着实现起来很简单吧!其实感觉这个东西本来就不难,关键是怎么用吧。还有就是匿名类。如果定义了一个匿名类,并想使用在匿名类内部使用外部类定义的一个对象,编译器要求外部类对象必须为final类型的(想想为什么?http://blog.csdn.net/cnitcastsdut/article/details/6020221)。这种写法是为了方便吧:

 1 interface Destination {
2 String readLabel();
3 }
4 public class test {
5 public Destination dest(final String dest) {
6 return new Destination() {
7 private String label = dest;
8 public String readLabel() { return label; }
9 };
10 }
11 public static void main(String[] args) {
12 test p = new test();
13 Destination d = p.dest("Tanzania");
14 }
15 }

威为什么内部类可以访问封装类?是因为在内部类的对象里面保存了一个外部类的指针。内部类拥有对封装类所有元素的访问权限。那如果内部类是“static”呢?创建一个static的内部类对象是不需要一个外部类对象,所以不能从static内部类的一个对象访问一个外部类对象的。

 1 abstract class Contents {
2 abstract public int value();
3 }
4 interface Destination {
5 String readLabel();
6 }
7 public class test {
8 private static class PContents extends Contents {
9 private int i = 11;
10 public int value() { return i; }
11 }
12 protected static class PDestination implements Destination {
13 private String label;
14 private PDestination(String whereTo) {
15 label = whereTo;
16 }
17 public String readLabel() { return label; }
18 }
19 public static Destination dest(String s) {
20 return new PDestination(s);
21 }
22 public static Contents cont() {
23 return new PContents();
24 }
25 public static void main(String[] args) {
26 Contents c = cont();
27 Destination d = dest("Tanzania");
28 }
29 }

怎么去声明一个内部类的对象?其实看起来并不奇怪哈,其实继承内部类的时候也是这么写的。更神奇的是内部类可以覆盖(hh)。

 1 public class test {
2 class Contents {
3 private int i = 11;
4 public int value() { return i; }
5 }
6 class Destination {
7 private String label;
8 Destination(String whereTo) {
9 label = whereTo;
10 }
11 String readLabel() { return label; }
12 }
13 public static void main(String[] args) {
14 test p = new test();
15 // Must use instance of outer class
16 // to create an instances of the inner class:
17 test.Contents c = p.new Contents();
18 test.Destination d = p.new Destination("Tanzania");
19 }
20 }


相关阅读:
Top