Thinking in Java 3rd Edition
g12544 1 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
致读者,
我从2002年7月开始翻译这本书,当时还是第二版。但是翻完前言和介绍部分后,chinapub就登出广告,说要出版侯捷的译本。于是我中止了翻译,等着侯先生的作品。
我是第一时间买的 这本书,但是我失望了。比起第一版,我终于能看懂这本书了,但是相比我的预期,它还是差一点。所以当Bruce Eckel在他的网站上公开本书的第三版的时候,我决定把它翻译出来。
说说容易,做做难。一本1000多页的书不是那么容易翻的。期间我也曾打过退堂鼓,但最终还是全部翻译出来了。从今年的两月初起,到7月底,我几乎放弃了所有的业余时间,全身心地投入本书的翻译之中。应该说,这项工作的难度超出了我的想像。
首先,读一本书和翻译一本书完全是两g11733g1119。g14533g16833g994中g7003是两g12193不g2528的g16833言,
g11004g14533g16833说g5483g5468g11033的g2489g4388,翻g6116中g7003之后就完全g11784了相。有时我g5483g14469g3921几分g19059,
g11004中g7003g18337g17860一g2489我能g11004几g12198g19059读懂的g2489g4388。g7368g1321g1929作g1038读g13785,一两g2489g16817g8821g6642
懂,g5194不g5445g2721g1332g10714g16311g6984本书,但g4557译g13785来说,这就不一g7691了。
g1866g8437,这是一本g16774g14533g16833的g1166g1901g13485g16774g14533g16833的g1166的书,所以g2528g5468多要g10043g20050g19762g14533g16833读
g13785的g6228g7427g7003g7735不g2528,它在g11004g16801,g2489g5347g7053g19766g19762g5132g19555g5859。g14533g16833读g13785g1262g5468g8439g17187这一点,但是g4557g3818g3281读g13785来说,这就是g17139g6297了。
g1889有,Bruce Eckel这g7691的g3835g10287g1166,g1901了1000多页,g3926g7536g18129g16765g1332读懂,他g4694不是
g3838g8821g19766g4388g731所以,书g18336还有一g1135g5468有g256g12121g5859g257的g2489g4388。比g3926那g2489g14891g2529的g256The
genesis of the computer revolution was in a machine,The genesis of our
programming languages thus tends to look like that machine.g257我就一g11464g8821g2519g1946该g5602
么翻译。我想g3835g8022g8821g1166能g2519g1946,说不定Bruce要的就是这g1022g6940g7536。
这是一本公g16760的g2529g14891,作g13785在g6228g7427上的g17908g16823g7092g2499g6373g2088。g13792作g1038译g13785,我的g13546g12255
能g2159差了g5468多。g1889g2164上上g19766g16774的这g1135g2419g3252,g1363g5483我不g5483不g7696g3818的g16892g5922。当我g18337
读初g12307的时候,我g2469g10628g19668要g1474g6925的地g7053g4466在g3838多了。g3252g8504,我不能g10628在就公开全部译g12307,我g2494能公开g5062g13475g1474g6925过的部分。不过这不是最终的版本,我还g1262g13499
g13505g1474g16758的。
本来,我g1946g3803到10月g1233,等我g1474g6925完前7g12468之后g1889公开。但是,我g2469g10628我g2460有点要放弃了,g3252g8504我决定g13485g14270g5061一点压g2159,g10628在就公开。以后,我将g1474g6925完一
g12468就公开一g12468,请关注www.wgqqh.com/shhgs/tij.html。
g3926g7536g1332觉g5483g3921,请g13485告诉我,g1332的鼓励是我工作的动g2159;g3926g7536g1332觉g5483不g3921,那就g7368应该告诉我了,我g1262参考g1332的g5859见作g1474g6925的。我希望能通过这g12193g7053法,译出一本配g5483上g2419g14891的书。
shhgs
2003年9月8日
Chapter 7,Polymorphism
g12544 2 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
7,多态性
多态性是g13499数据抽象和g13499承之后的,g19766向g4557象的g13546g12255g16833言的第三g1022基本特性。
它提供了另一g1022层g19766的接口g994g4466g10628的分离,也就是说把做什么和g5602么做分开来。多态性不但能g6925善代g11733的结构,提高g1866g2499读性,g13792且能g16765g1332创建g2499
扩展的(extensible)g12255序。所谓g256g2499扩展g257是指,g12255序不仅在项目最初的开g2469阶段能g256g6116长g257,g13792且还g2499以在g19668要添g2164新特性的时候g256g6116长g257。
g256封装g257通过将数据的特征g994行g1038结合在一起,创建了一g12193新的数据类型。g256隐藏g4466g10628g257通过将细节设g6116private,完g6116了接口g994g4466g10628的分离。
之所以要采取这g12193比较呆板的顺序来g16774g16311,是要g10043g20050那g1135过g12255g16833言的g12255序员们。但是,多态性是站在g256类g257的角度来处g10714这g12193逻辑上的分离的。在上一g12468中,g1332看到了,g256g13499承g257是g5602g7691允许g1332将g4557象当作它g14270g5061的,或g13785
它的基类的类型来处g10714的。这是一g1022g5468g18337要的功能,g3252g1038它能g16765g1332把多g1022
类(派生g14270g2528一g1022基类的)当作一g1022类来处g10714,这g7691一段代g11733就能作g11004于g5468
多不g2528的类型了。g256多态g7053法调g11004(polymorphic method call)g257能g16765
类表g10628出各g14270所独有的特点,g2494要这g1135类g18129是从g2528一g1022基类g18336派生出来的就行了。当g1332通过基类的reference调g11004g7053法的时候,这g1135不g2528就g1262通过行g1038表g10628出来。
本g12468g1262从基础开始,通过一g1135简单的,g2494涉及多态行g1038的g12255序,来g16774g16311多态性(也被称g1038动态绑定『dynamic binding』、后绑定『late
binding』或运行时绑定『run-time bingding』)。
再访上传(upcasting)
g1332g5062g13475在第6g12468看到,g5602g7691把g4557象当作它g14270g5061的或是它的基类的g4557象来g1363
g11004。把g4557象的reference当作基类的reference来g11004,被称g1038上传
(upcasting)。g3252g1038在g13499承关系图中,基类总是在上g19766的。
但是问题也来了。下g19766我们g11004乐器来举例。由于乐器要演奏Note(音符),所以我们在packageg18336单独创建一g1022Note类,
//,c07:music:Note.java
// Notes to play on musical instruments,
package c07.music;
import com.bruceeckel.simpletest.*;
public class Note {
private String noteName;
private Note(String noteName) {
this.noteName = noteName;
}
public String toString() { return noteName; }
Thinking in Java 3rd Edition
g12544 3 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
public static final Note
MIDDLE_C = new Note("Middle C"),
C_SHARP = new Note("C Sharp"),
B_FLAT = new Note("B Flat");
// Etc,
} ///:~
这是一g1022g256枚举(enumeration)g257类,它创建了几g1022固定g4557象以供选择。g1332不能g1889创建g1866它g4557象了,g3252g1038构g17908函数是private的。
在接下去的g12255序中,Wind作g1038一g12193乐器g13499承了Instrument,
//,c07:music:Music.java
// Inheritance & upcasting,
package c07.music;
import com.bruceeckel.simpletest.*;
public class Music {
private static Test monitor = new Test();
public static void tune(Instrument i) {
//,.,
i.play(Note.MIDDLE_C);
}
public static void main(String[] args) {
Wind flute = new Wind();
tune(flute); // Upcasting
monitor.expect(new String[] {
"Wind.play() Middle C"
});
}
} ///:~
//,c07:music:Wind.java
package c07.music;
// Wind objects are instruments
// because they have the same interface,
public class Wind extends Instrument {
// Redefine interface method,
public void play(Note n) {
System.out.println("Wind.play() " + n);
}
} ///:~
Music.tune( )g19668要一g1022Instrument的reference作参数,但是它也g2499以接受任g1321由Instrument派生出来的reference。当main( )
未g13475g17728g6454就把Wind的reference传g13485tune( )的时候,这一g2011就g2469生了。这是完全g2499以g2499行的g252g252g3252g1038Windg13499承了Instrument,g3252g8504它
g5529g20047g4466g10628Instrument的接口。从Wind上传到Instrument的时
Chapter 7,Polymorphism
g12544 4 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
候,接口g2499能g1262g256g2476g12376g257,但是g1889g4579也不g1262比Instrument的接口g7368
g4579。
把对象的类型忘掉
g2499能g1332g1262觉g5483Music.java有g1135g3867g5630。g1038什么g1262有g1166要g6937g5859g256g5548g6493g257g4557
象的类型g2614g731上传就是在做这g1226g1119g5785。但是,g16765tune( )g11464接g6355Wind
的reference作参数g3921像g7368简单一g1135。但是这g1262有一g1022问题:g3926g7536采g11004
这g12193g7053法,g1332就g5483g1038系g13491g18336的g8611g1022Instrumentg18129g1901一g1022新的tune( )
g7053法。g1563设我们顺着这g1022g5617g17347,g1889g2164一g1022Stringed (g5370乐器)和一g1022
Brass (g12661乐器),
//,c07:music:Music2.java
// Overloading instead of upcasting,
package c07.music;
import com.bruceeckel.simpletest.*;
class Stringed extends Instrument {
public void play(Note n) {
System.out.println("Stringed.play() " + n);
}
}
class Brass extends Instrument {
public void play(Note n) {
System.out.println("Brass.play() " + n);
}
}
public class Music2 {
private static Test monitor = new Test();
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);
monitor.expect(new String[] {
"Wind.play() Middle C",
"Stringed.play() Middle C",
"Brass.play() Middle C"
});
}
} ///:~
Thinking in Java 3rd Edition
g12544 5 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
这g12193做法不是不g2499以,但是有g1022g18337g3835g13582g19531:g8611g8437g2164入新的Instrument
的时候,g1332g18129g5529g20047g999g19388g1038这g1022类g1901一g1022g7053法。这就g5859g2631着,不但定g1053类的时候要多g1901代g11733,g13792且添g2164tune( ) 之类的g7053法,或g13785添g2164新的
Instrument的时候,还g1262多出g5468多g1119g5785。g8504g3818,g3926g7536g1332g5548了g18337g17745g7588g1022
g7053法,g13546译器是不g1262g6265g19181的,于是类型处g10714工作就完全g1093g3883了。
g3926g7536g1332g2499以g1901g2494一g1022g11004基类,g13792不是g1867g1319的派生类作参数的g7053法,那g1262不
g1262g7368g3921一g1135g2614g731也就是,g3926g7536g1332g2499以g5548g6493它们g18129是派生类,g2494g1901g2528基类打
g1144g17959的代g11733,那g1262不g1262g7368g3921g2614g731
这就是多态性要g16311决的问题。g9994g13792g13489g3835多数从过g12255g16833言g17728过来的g12255序员们,在g10714g16311多态性的运行g7438g2058的时候,g18129g1262g1135问题。
问题的关键
Music.javag16765g1166觉g5483g17165g16311的地g7053就是,运行的时候,g11507g8503g1147生g17767出的是
Wind.play( )。g16814g9994,这g8503是我们所希望,但是它g2376g8821有告诉我们它g1038
什么要这g7691运行。看看tune( )g7053法,
public static void tune(Instrument i) {
//,.,
i.play(Note.MIDDLE_C);
}
它接受一g1022Instrument的reference做参数。那么g13546译器g5602么g1262g11705g17959
这g1022Instrument的reference就指向一g1022Wind,g13792不是Brass或
Stringedg2614g731g13546译器不g2499能g11705g17959。g1038了能g9157入的g10714g16311这g1022问题,我们先来看看什么是g256绑定(binding)g257。
方法调用的绑定
将g7053法的调g11004g17842到g7053法本身被称g1038g256绑定(binding)g257。当绑定g2469生在g12255
序运行之前时(g3926g7536有的g16817,就是由g13546译器或g17842接器g17139g17143)被称作g256前绑定
(early binding)g257。g2499能g1332从g8821g2560说过这g1022g7427g16833,g3252g1038g19766向过g12255的g16833言
g7693本就g8821有这g1022g8022g5577。C的g13546译器g2494允许一g12193g7053法调g11004,那就是前绑定。
上g17860例g12255之所以g1208g1166g17165g16311g18129是g9316于前绑定,g3252g1038当g13546译器g2494有一g1022
Instrument的reference的时候,它是不g11705g17959该g17842到g2750g1022g7053法的。
g16311决g7053g7708就是g256后绑定(late binding)g257,它的g5859g5617是要在g12255序运行的时候,g7693据g4557象的类型来决定该绑定g2750g1022g7053法。后绑定也被称g1038g256动态绑定
(dynamic binding)g257或g256运行时绑定(run-time binding)g257。g3926g7536g16833
言g4466g10628了后绑定,那它就g5529g20047要有能在运行时g2040g7041g4557象类型,g5194且调g11004g1866
Chapter 7,Polymorphism
g12544 6 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
合g17878的g7053法的g7438g2058。也就是说,g13546译器还是不g11705g17959g4557象的类型,但是g7053法的调g11004g7438g2058g1262g6226出,g5194且调g11004g8503g11842的g7053法。后绑定g7438g2058g1262g19555g16833言的不g2528g13792
不g2528,但是g1332g2499以设想,g4557象g18336g19766g5529定g4396有g256它g4658于g2750g12193类型g257的g1461g5699。
g19512了static和finalg7053法(privateg7053法隐g2559有final的g5859g5617),Java
的所有的g7053法g18129采g11004后绑定。也就是说,通g5132g5785g1929下g1332不g5529考g15397是不是应该采g11004后绑定g252g252它是g14270动的。
g1038什么要g3780g7138finalg7053法g731我们在上一g12468指出,这g7691g2499以g12117止g2047g1166g16218g1901那
g1022g7053法。不过,g7368g18337要的g2499能还是要g256关g19393g257它的动态绑定,或g13785g7368g11842g2011
的说,告诉g13546译器这g18336不g19668要g1363g11004后绑定。这g7691g13546译器就能g1038finalg7053法生g6116g12257g5506高g6940一g1135的调g11004代g11733。g9994g13792在g13489g3835多数g5785g1929下,这g12193做法g5194不g1262
g4557g12255序的总g1319性能g1147生什么g5445g2721,g3252g8504最g3921还是g2494把final当作一g12193设g16757
g6175段来g11004,g13792不要去考g15397g11004它来提高性能。
产生正确的行为
一g7098g11705g17959Java通过后绑定g4466g10628了多态的g7053法调g11004,g1332就g2499以g2494g13546g1901g2528基类打g1144g17959的代g11733了。g3252g1038g1332g11705g17959所有的派生类也能g8503g11842地g1363g11004这g1135代g11733。
或g13785g6454一g1022说法,g1332g256向g4557象g2469一g1022g9052g5699,g16765它g14270g5061g2040g7041该做g1135什么。g257
g256g5430g10378g257 就是g16774g16311OOP的一g1022g13475g1868的例g4388。它看起来g11464g16278,g3252g8504被广
g8879g1363g11004,但是不g5196的是,这g1262g16765新g6175g16835以g1038OOPg2494是g11004来处g10714图像g13546g12255
的,这g7186g9994不g4557。
在这g1022例g4388中,基类被称作Shape,它有g3921几g1022派生类:Circle,
Square,Triangle,等等。这g1022例g4388之所以g3921,是g3252g1038我们能g5468g14270g9994
地说g256g3290g5430是一g12193g5430g10378g257,g13792g2560的g1166也能g7138g11345。下g19766的g13499承关系图g1319g10628了这g12193关系,
Thinking in Java 3rd Edition
g12544 7 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
下g19766这g2489就是在g256上传g257,
Shape s = new Circle();
这g18336先创建了一g1022Circleg4557象,接着g20544上把它的referenceg17183g13485了
Shape。看上去这像是一g1022g19181g16835(一g12193类型g5602么能g17183g13485另一g12193);但是由于Circle是由Shape派生出来的,Circle就是一g12193Shape,g3252g8504这
g12193做法g19762g5132g8503g11842。所以g13546译器g1262g8639不g2559g12958地接受这g7477g16833g2489,什么g19181g18129不
g6265。
g1563设g1332调g11004了一g1022基类g7053法(派生类g5062g13475g16218g1901这g1022g7053法),
s.draw();
g2499能g1332g1262g16760g1038,这g8437应该总调g11004Shape的draw( )了g2555,g3252g1038g8617g12467这是Shape的referenceg252g252g13546译器g2460g5602么g1262g11705g17959还要做g1866它g1119g5785g2614g731但是由于g4466g10628了后绑定(多态性),g4466g19481上它g1262调g11004Circle.draw( )。
下g19766的例g12255g12257g5506作了一g1135g2476g2282,
//,c07:Shapes.java
// Polymorphism in Java,
import com.bruceeckel.simpletest.*;
import java.util.*;
class Shape {
void draw() {}
void erase() {}
}
class Circle extends Shape {
void draw() {
System.out.println("Circle.draw()");
}
void erase() {
System.out.println("Circle.erase()");
}
}
class Square extends Shape {
void draw() {
System.out.println("Square.draw()");
}
void erase() {
System.out.println("Square.erase()");
}
}
class Triangle extends Shape {
void draw() {
Chapter 7,Polymorphism
g12544 8 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
System.out.println("Triangle.draw()");
}
void erase() {
System.out.println("Triangle.erase()");
}
}
// A "factory" that randomly creates shapes,
class RandomShapeGenerator {
private Random rand = new Random();
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 Test monitor = new Test();
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(int i = 0; i < s.length; i++)
s[i].draw();
monitor.expect(new Object[] {
new TestExpression("%%
(Circle|Square|Triangle)"
+ "\\.draw\\(\\)",s.length)
});
}
} ///:~
基类Shapeg1038g13499承类定g1053了一g1022g1861g11004的接口g252g252也就是说,所有的
Shapeg18129有draw( )和erase( )这两g1022g7053法。派生类g1262g16218g1901这两g1022g7053
法,以提供各g14270所独有的行g1038。
RandomShapeGenerator是一g1022g256工g2390g257,g8611g8437调g11004它的next( )
g7053法的时候,它g18129g1262g19555g7438选取一g1022Shapeg4557象,g9994后g17832g3250这g1022g4557象的
reference。要注g5859,上传就g2469生在returng16833g2489,它g6355到的g18129是
Circle,Square或是Triangle,g13792它g17832g3250的g18129是Shape。g3252g8504g8611g8437
调g11004next( )的时候,g1332g18129g8821法g11705g17959g17832g3250g1552的g1867g1319类型,g3252g1038传g13485g1332的g8716
g17840是g7234通的Shape reference。
main( )创建了一g1022Shape reference的数g13464,g9994后g11004
RandomShapeGenerator.next( )把它g3647g9397。这时,g1332g2494g11705g17959它们
Thinking in Java 3rd Edition
g12544 9 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
g18129是Shape,g14279于g7368g1867g1319的,就不g5483g13792g11705了(g13546译器也一g7691)。但是一g7098
g12255序运行,当g1332g17953g2394数g13464,g17892g1022地调g11004它们的draw( )g7053法的时候,g1332
就g1262g2469g10628,draw( )的行g1038g20776法g14336地g2476g6116了各g1022g1867g1319类型的g8503g11842行g1038
了。
之所以要g19555g7438选择g5430g10378,是想做g5483g17959地一点,g16765g1332相g1461g13546译器在g13546译的时候也不g11705g17959该选g11004g2750g1022g7053法。所有的draw( )g18129g5529g20047g1363g11004后绑定。
可扩展性
g10628在,我们g1889g3250到乐器的例g4388。由于有了多态性,g1332就g2499以g7693据g19668要,g5460
系g13491g18336添g2164任g5859多g1022新类型,g13792不g11004g6297心还要g1474g6925tune( )g7053法了。在一
g1022设g16757g14403g3921的OOPg12255序中,g13489g3835多数g7053法g18129g1262和tune( )一g7691,g2494g17331基类接口打g1144g17959。这g12193g12255序是g2499扩展的,g3252g1038g1332g2499以通过g256g16765新的数据类型
g13499承通g11004的基类g257的g7053法,来添g2164新的功能。g13792那g1135g994基类接口打g1144g17959的
g7053法,g7693本不g19668要作g1474g6925就能g17878应新的类。
就g6355乐器g1038例,想想该g5602g7691g5460基类g18336g2164新的g7053法,g5194且通过g13499承g1147生一g1135
新的类。下g19766就是关系图,
Chapter 7,Polymorphism
g12544 10 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
这g1135新的类g18129能g2528g2419先未作g1474g6925的tune( )g7053法协g2528工作。即便是在
tune( )保g4396在另一g1022g7003g1226,g13792Instrument的接口g5062g13475g2164入了新g7053法的g5785g1929下,它也能g7092g20047g18337新g13546译g13792g8503g5132工作。下g19766就是这g1022关系图的g4466
g10628,
//,c07:music3:Music3.java
// An extensible program,
package c07.music3;
import com.bruceeckel.simpletest.*;
import c07.music.Note;
class Instrument {
void play(Note n) {
System.out.println("Instrument.play() " + n);
}
String what() { return "Instrument"; }
void adjust() {}
}
class Wind extends Instrument {
void play(Note n) {
System.out.println("Wind.play() " + n);
}
String what() { return "Wind"; }
void adjust() {}
}
class Percussion extends Instrument {
void play(Note n) {
System.out.println("Percussion.play() " + n);
}
String what() { return "Percussion"; }
void adjust() {}
}
class Stringed extends Instrument {
void play(Note n) {
System.out.println("Stringed.play() " + n);
}
String what() { return "Stringed"; }
void adjust() {}
}
class Brass extends Wind {
void play(Note n) {
System.out.println("Brass.play() " + n);
}
void adjust() {
System.out.println("Brass.adjust()");
}
}
class Woodwind extends Wind {
void play(Note n) {
System.out.println("Woodwind.play() " + n);
}
String what() { return "Woodwind"; }
}
Thinking in Java 3rd Edition
g12544 11 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
public class Music3 {
private static Test monitor = new Test();
// Doesn't care about type,so new types
// added to the system still work right,
public static void tune(Instrument i) {
//,.,
i.play(Note.MIDDLE_C);
}
public static void tuneAll(Instrument[] e) {
for(int i = 0; i < e.length; i++)
tune(e[i]);
}
public static void main(String[] args) {
// Upcasting during addition to the array,
Instrument[] orchestra = {
new Wind(),
new Percussion(),
new Stringed(),
new Brass(),
new Woodwind()
};
tuneAll(orchestra);
monitor.expect(new String[] {
"Wind.play() Middle C",
"Percussion.play() Middle C",
"Stringed.play() Middle C",
"Brass.play() Middle C",
"Woodwind.play() Middle C"
});
}
} ///:~
新的g7053法是what( )和adjust( )。前g13785g1262g17832g3250一g1022描g17860这g1022类的
String;g13792后g13785则g1262g1038g8611g1226乐器调音。
当g1332向main( )的orchestra数g13464放东西的时候,它们g18129g1262被g14270动地上传到Instrument。
g2499以看到,tune( )g7053法g4557周遭所g2469生的g2476g2282一g7092所g11705,但是g2376能g8503g5132工作。这g8503是我们所希望的,多态性应该提供的功能。g12255序的g2476动不g1262g5445g2721
到它不应该g5445g2721的部分。g6454言之,g4557g12255序员来说,多态性是一项g19762g5132g18337要的g6228g7427,它能g16765g1332g256将g1262g2476和不g1262g2476的东西分隔开来。g257
错误:“覆写”private的方法
g1332g2499能g1262g5468g7092辜地尝试去作下g19766这类g1119g5785,
//,c07:PrivateOverride.java
// Abstract classes and methods,
import com.bruceeckel.simpletest.*;
public class PrivateOverride {
Chapter 7,Polymorphism
g12544 12 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
private static Test monitor = new Test();
private void f() {
System.out.println("private f()");
}
public static void main(String[] args) {
PrivateOverride po = new Derived();
po.f();
monitor.expect(new String[] {
"private f()"
});
}
}
class Derived extends PrivateOverride {
public void f() {
System.out.println("public f()");
}
} ///:~
g2499能g1332预想的g17767出g1262是g256public f( )g257,但是privateg7053法g14270动就是
final的,g13792且g1262g4557派生类隐藏。g3252g8504这g18336Derived的f( )是一g1022全新的g7053法;甚g14279g17842g18337g17745g18129不算,g3252g1038Derivedg7693本看不到基类的f( )。
结论就是,g2494有g19762private的g7053法才能被g16218g1901。但是g1332g5483留g5859那g1135看上去像是在g16218g1901privateg7053法的g12255序,它们不g1262g1147生g13546译g19181g16835,但是g5468g2499
能g1262不按g1332的g16757划运行。说g5483g1889彻底一g1135,g2047g11004基类的privateg7053法的
g2529字去命g2529派生类的g7053法。
抽象类和抽象方法
在这g1135乐器例g4388中,Instrument基类的g7053法g18129是g1135g256g7691g4388货g257。g3926g7536
这g1135g7053法g11507的被调g11004了,那g12255序就有问题了。g4466g19481上Instrument的g5859
图是要g1038所有由它派生出来的类创建一g1022公g1861的接口。
要创建这g12193公g1861接口的唯一g2419g3252就是,各g1022g4388类要g11004它g14270g5061的g7053g5347来g4466g10628
这g1022接口。它定g1053了一g1022基本的g5430g5347,g1332g2499以说这是所有的派生类所g1861有的。还有一g12193说法,就是Instrument是一g1022g256抽象的基类(abstract
base class或g13785简g2282g1038抽象类abstract class)g257。当g1332想要通过一g1022公
g1861的接口来操控一g13464类的时候,就g2499以g1363g11004抽象类了。通过动态绑定g7438
g2058,那g1135符合g7053法特征的派生类g7053法将g1262g5483到调g11004。(g8503g3926g1332在上一节看到的,g3926g7536g7053法g2529g2528基类的相g2528,g13792参数列表不g2528,那就g2476g6116g18337g17745了,这
g3835g8022不是g1332想要的结g7536g2555。)
g3926g7536g1332有一g1022像Instrument这g7691的abstract class,那么这g12193类的g4557
象是g8821什么g5859g5617的。也就是说Instrumentg2494是g11004来定g1053接口,g13792不是
g1867g1319g4466g10628,g3252g8504创建Instrumentg4557象g8821有g5859g1053,g7368g1321g1929g1332还g2499能要g12117
止g11004户这么作。要做到这点,g2499以g16765Instrument的g7053法打印g19181g16835g1461
Thinking in Java 3rd Edition
g12544 13 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
g5699,但是这g7691一来就把问题留到运行时了,g3252g8504g11004户端g19668要进行详尽的测试。g7368g3921的办法还是在g13546译时g2469g10628这g1022问题。
Java提供了一g12193被称g1038g256抽象g7053法(abstract method)g257的g7438g2058来g16311决这g1022问题。[32]这是一g12193尚未完g6116的g7053法;这g12193g7053法g2494有g3780g7138,g8821有g8503
g7003。下g19766就是抽象g7053法的g3780g7138,
abstract void f();
包g2559抽象g7053法的类被称g1038g256抽象类(abstract class)g257。g3926g7536类包g2559一g1022
或多g1022抽象g7053法,那么这g1022类就g5529g20047被定g1053g6116abstract的。(否则g13546译器就g1262g6265g19181了。)
既g9994抽象类是尚未完g6116的类,那么g3926g7536有g1166想要创建抽象类的g4557象的g16817,
g13546译器g2460打算g5602么做g2614g731g13546译器g8821法安全地创建抽象类g4557象,所以它g1262g6265
g19181。由g8504,g13546译器保证了抽象类的纯粹性,g13792g1332也不g11004g6297心它g1262被g16835g11004
了。
g3926g7536g1332g13499承了抽象类,g13792且还打算创建这g1022新类的g4557象,那g1332就g5529g20047g4466g10628
基类所定g1053的全部抽象g7053法。g3926g7536g1332不这么做(g1332g11842g4466g2499以选择不这么做),那么这g1022g13499承下来的类就也g6116了抽象类了,g13546译器g1262强g2058g1332g11004
abstract关键g16801来g3780g7138这g1022类的。
创建一g1022不包g2559abstractg7053法的abstract类,是完全g2499以的。这g12193
g6228巧g2499以g11004于g256不g5529创建abstract的g7053法,但是g2460要g12117止g2047g1166创建这g1022
类的g4557象g257的场合。
Instrument类g2499以g5468容易的被g1474g6925g6116abstract类。由于抽象类g5194不要求它的所有g7053法g18129是抽象的,g3252g8504g2494要把几g1022g7053法定g1053g6116abstract的就行了。下g19766就是设g16757g7053g7708,
Chapter 7,Polymorphism
g12544 14 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
就是g11004abstract类和abstractg7053法来g1474g6925orchestra (g12661g5370乐团)的例g4388,
//,c07:music4:Music4.java
// Abstract classes and methods,
package c07.music4;
import com.bruceeckel.simpletest.*;
import java.util.*;
import c07.music.Note;
abstract class Instrument {
private int i; // Storage allocated for each
public abstract void play(Note n);
public String what() {
return "Instrument";
}
public abstract void adjust();
}
class Wind extends Instrument {
public void play(Note n) {
System.out.println("Wind.play() " + n);
}
public String what() { return "Wind"; }
public void adjust() {}
}
class Percussion extends Instrument {
Thinking in Java 3rd Edition
g12544 15 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
public void play(Note n) {
System.out.println("Percussion.play() " + n);
}
public String what() { return "Percussion"; }
public void adjust() {}
}
class Stringed extends Instrument {
public void play(Note n) {
System.out.println("Stringed.play() " + n);
}
public String what() { return "Stringed"; }
public void adjust() {}
}
class Brass extends Wind {
public void play(Note n) {
System.out.println("Brass.play() " + n);
}
public void adjust() {
System.out.println("Brass.adjust()");
}
}
class Woodwind extends Wind {
public void play(Note n) {
System.out.println("Woodwind.play() " + n);
}
public String what() { return "Woodwind"; }
}
public class Music4 {
private static Test monitor = new Test();
// Doesn't care about type,so new types
// added to the system still work right,
static void tune(Instrument i) {
//,.,
i.play(Note.MIDDLE_C);
}
static void tuneAll(Instrument[] e) {
for(int i = 0; i < e.length; i++)
tune(e[i]);
}
public static void main(String[] args) {
// Upcasting during addition to the array,
Instrument[] orchestra = {
new Wind(),
new Percussion(),
new Stringed(),
new Brass(),
new Woodwind()
};
tuneAll(orchestra);
monitor.expect(new String[] {
"Wind.play() Middle C",
"Percussion.play() Middle C",
"Stringed.play() Middle C",
"Brass.play() Middle C",
"Woodwind.play() Middle C"
});
Chapter 7,Polymorphism
g12544 16 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
}
} ///:~
g2499以看到,g19512了基类之g3818,g2047的什么g18129g8821g6925。
由于它g7138g11842了类的抽象性,g5194且告诉g11004户和g13546译器该g3926g1321g1363g11004,g3252g8504
abstract的类和g7053法能帮g1332g16311决g5468多问题。
构造函数与多态性
g17331g5460g5132一g7691,构g17908函数总是g994众不g2528,牵涉到多态性的时候也不例g3818。尽
g12661构g17908函数不是多态的(g4466g19481上它们g18129是staticg7053法,g2494是g3780g7138的时候g8821
有g11464说),但是能g10714g16311g256它在复杂的类系和多态的环境下是g3926g1321工作的g257
仍g9994十分g18337要。一g7098g10714g16311了这g1022问题,g1332就能避开g5468多g16765g1166不舒服的纠缠。
构造函数的调用顺序
我们先是在第4g12468简要介绍了构g17908函数的调g11004顺序,后来g2460在第6g12468作了进一步的g16774g16311,但是那时我们还g8821有介绍多态性。
在创建派生类g4557象的过g12255中,基类的构g17908函数总是先g5483到调g11004,这g7691一级一级的追溯上去,g8611g1022基类的构g17908函数g18129g1262被调g11004。这g12193做法是g5468合乎g5785
g10714的,g3252g1038构g17908函数有一g1022特殊的任务:它要g11705g17959g4557象是不是被g8503g11842地创建了。派生类g2494能访问它g14270g5061的g6116员,它看不到基类的g6116员(g3252g1038它们通
g5132g18129是private的)。g2494有基类的构g17908函数才g11705g17959g5602g7691初始g2282它的g6116员,
g2528时也g2494有它才有权限进行初始g2282。g3252g8504g256把所有的构g17908函数g18129调g11004一
g17953g257就g2476g5483g19762g5132g18337要了,否则g4557象就g8821法创建了。这就是g1038什么g13546译器g1262
强g2058g8611g1022派生类g18129要调g11004g1866基类的构g17908函数的g2419g3252了。g3926g7536g1332不在派生类的构g17908函数g18336g7138g11842地调g11004基类的构g17908函数,那g13546译器就g1262悄悄的调g11004那g1022
默g16760的构g17908函数。g3926g7536g8821有默g16760构g17908函数,g13546译器就g1262g6265g19181。(要是类g8821
有构g17908函数,g13546译器g1262g14270动g1038g1332g1946g3803一g1022默g16760构g17908函数。)
我们来看看下g19766这g1022例g4388,通过它我们g2499以了g16311合g6116,g13499承,以及多态性
g4557于g4557象创建的g5445g2721,
//,c07:Sandwich.java
// Order of constructor calls,
package c07;
import com.bruceeckel.simpletest.*;
class Meal {
Meal() { System.out.println("Meal()"); }
}
Thinking in Java 3rd Edition
g12544 17 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
class Bread {
Bread() { System.out.println("Bread()"); }
}
class Cheese {
Cheese() { System.out.println("Cheese()"); }
}
class Lettuce {
Lettuce() { System.out.println("Lettuce()"); }
}
class Lunch extends Meal {
Lunch() { System.out.println("Lunch()"); }
}
class PortableLunch extends Lunch {
PortableLunch()
{ System.out.println("PortableLunch()");}
}
public class Sandwich extends PortableLunch {
private static Test monitor = new Test();
private Bread b = new Bread();
private Cheese c = new Cheese();
private Lettuce l = new Lettuce();
public Sandwich() {
System.out.println("Sandwich()");
}
public static void main(String[] args) {
new Sandwich();
monitor.expect(new String[] {
"Meal()",
"Lunch()",
"PortableLunch()",
"Bread()",
"Cheese()",
"Lettuce()",
"Sandwich()"
});
}
} ///:~
这段g12255序g11004g5468多类创建了一g1022复杂的类,这g1135类g18129有构g17908函数。
Sandwich是最g18337要的类,它有三层g13499承关系(g3926g7536g1332把隐g2559的g13499承
Object也算上的g16817,g4466g19481上是四层),还有三g1022g6116员g4557象。g1332g2499以通过
main( )的g17767出g16278察到Sandwichg4557象的创建过g12255。也就是说复杂g4557象的构g17908函数的调g11004顺序是这g7691的,
1,调g11004基类的构g17908函数。这是一g1022递归过g12255,g3252g8504g1262先创建g13499承g1319系的g7693,g9994后是下一级派生类,以g8504类推,g11464到最后一g1022g13499承类的构
g17908函数。
2,g6116员g4557象按g10043g1866g3780g7138的顺序进行初始g2282。
3,执行g13499承类的构g17908函数的g8503g7003。
Chapter 7,Polymorphism
g12544 18 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
构g17908函数的调g11004顺序是g19762g5132g18337要的。g13499承的前提就是g1332能看到基类,g5194且还能访问它的public和protectedg6116员。也就是说,轮到处g10714派生类的时候,所有的基类g6116员g18129应该g5062g13475g1946g3803g3921了。g4557于g7234通g7053法,构g17908过
g12255g5062g13475结束了,g3252g8504g4557象g5062g13475完全建g3921了。但是g4557构g17908函数来说,这还g2494
是g1563设。g1038了g11842保这一点,g2494能g16765构g17908函数先调g11004基类的构g17908函数。这
g7691,当g1332执行派生类构g17908函数的时候,基类g6116员就g18129g5062g13475初始g2282完g8617了,
g2499以供g1332访问了。g256能g11004构g17908函数访问数据g6116员g257也是g256g2494要有g2499能,就在定g1053类的g6116员g4557象(也就是g11004合g6116的g7053g5347放进去的g4557象)的时候就进行初始g2282g257的g2419g3252(比g3926上g17860g12255序中的b,c,以及l)。g3926g7536g1332遵循了这g12193做法,那么当前g4557象的所有g256基类g6116员(base class members)g257,以及它
g14270g5061的g6116员g4557象的初始g2282就有保证了。但不g5196的是,这不是一g7477万能法则。下一节就g1262g16774到。
继承与清理
即便新类既有合g6116g2460有g13499承,g13489g3835多数g5785g1929下,g1332g18129g7092g20047g6297心清g10714的问题;g4388g4557象通g5132g18129g2499以g1144由垃圾g3250收器去处g10714。g3926g7536g11507的g19668要进行清g10714,
那就g2494能辛苦一点,g1038新类创建一g1022dispose( )g7053法了(我特地选了这
g1022g2529字,不过g1332也g2499以选一g1022g1332觉g5483g7368g3921的g2529字)。g13792且在g13499承g5785g1929下,
g3926g7536垃圾g3250收过g12255中还要作一g1135特殊的处g10714,那g1332还g5529g20047在派生类g18336g16218g1901
基类的dispose( )。当g1332g13546g1901派生类的dispose( )的时候,要记住第一g1226g1119就是调g11004基类的dispose( ),这点g19762g5132g18337要。g3252g1038不这g7691做的
g16817,基类就不g1262g5483到清g10714。下g19766的例g12255演示了这点,
//,c07:Frog.java
// Cleanup and inheritance,
import com.bruceeckel.simpletest.*;
class Characteristic {
private String s;
Characteristic(String s) {
this.s = s;
System.out.println("Creating Characteristic " +
s);
}
protected void dispose() {
System.out.println("finalizing Characteristic "
+ s);
}
}
class Description {
private String s;
Description(String s) {
this.s = s;
System.out.println("Creating Description " + s);
}
protected void dispose() {
System.out.println("finalizing Description " +
s);
}
}
Thinking in Java 3rd Edition
g12544 19 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
class LivingCreature {
private Characteristic p = new Characteristic("is
alive");
private Description t =
new Description("Basic Living Creature");
LivingCreature() {
System.out.println("LivingCreature()");
}
protected void dispose() {
System.out.println("LivingCreature dispose");
t.dispose();
p.dispose();
}
}
class Animal extends LivingCreature {
private Characteristic p= new Characteristic("has
heart");
private Description t =
new Description("Animal not Vegetable");
Animal() {
System.out.println("Animal()");
}
protected void dispose() {
System.out.println("Animal dispose");
t.dispose();
p.dispose();
super.dispose();
}
}
class Amphibian extends Animal {
private Characteristic p =
new Characteristic("can live in water");
private Description t =
new Description("Both water and land");
Amphibian() {
System.out.println("Amphibian()");
}
protected void dispose() {
System.out.println("Amphibian dispose");
t.dispose();
p.dispose();
super.dispose();
}
}
public class Frog extends Amphibian {
private static Test monitor = new Test();
private Characteristic p = new
Characteristic("Croaks");
private Description t = new Description("Eats
Bugs");
public Frog() {
System.out.println("Frog()");
}
protected void dispose() {
System.out.println("Frog dispose");
t.dispose();
Chapter 7,Polymorphism
g12544 20 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
p.dispose();
super.dispose();
}
public static void main(String[] args) {
Frog frog = new Frog();
System.out.println("Bye!");
frog.dispose();
monitor.expect(new String[] {
"Creating Characteristic is alive",
"Creating Description Basic Living Creature",
"LivingCreature()",
"Creating Characteristic has heart",
"Creating Description Animal not Vegetable",
"Animal()",
"Creating Characteristic can live in water",
"Creating Description Both water and land",
"Amphibian()",
"Creating Characteristic Croaks",
"Creating Description Eats Bugs",
"Frog()",
"Bye!",
"Frog dispose",
"finalizing Description Eats Bugs",
"finalizing Characteristic Croaks",
"Amphibian dispose",
"finalizing Description Both water and land",
"finalizing Characteristic can live in water",
"Animal dispose",
"finalizing Description Animal not Vegetable",
"finalizing Characteristic has heart",
"LivingCreature dispose",
"finalizing Description Basic Living Creature",
"finalizing Characteristic is alive"
});
}
} ///:~
这g1022g13499承g1319系中的g8611g1022类g18129有一g1022Characteristic和一g1022
Description类型的g6116员g4557象。它们也应该g5483到清g10714。g4557象g994g4557象之间有g2499能g1262有依赖关系,g3252g8504清g10714的顺序应该g994初始g2282的顺序相反。g4557数据
g6116员g13792言,这就是说它们的清g10714顺序应该g994g3780g7138的顺序相反(g3252g1038数据的初始g2282是按g10043g3780g7138的顺序进行的)。g4557基类g13792言(它采g11004了C++拆构函数的g5430g5347),g1332应该先进行派生类的清g10714,g1889进行基类的清g10714。这是g3252g1038派生类的清g10714g2499能g19668要调g11004g7588g1135基类的g7053法,也就是说要留着基类,g3252g8504它不能过早地被清g19512g6493。g1332g2499以从g12255序的g17767出看出,Frogg4557象各部分的清
g10714顺序,g8503g3921g994它们创建的顺序相反。
这g1022例g12255表g7138,尽g12661g1332不g1262老是进行清g10714,但是g11507的要做的时候,还是要
g19762g5132g4579心的。
多态方法在构造函数中的行为
Thinking in Java 3rd Edition
g12544 21 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
构g17908函数的调g11004顺序也带来了一g1022有趣的难题。g3926g7536构g17908函数调g11004了一g1022
动态绑定的g7053法,g13792这g1022g7053法g2460g4658于那g1022g8503在创建中的g4557象,那它g1262g1147生什么g7691的g6940g7536g2614g731g3926g7536是g7234通g7053法,g1332猜也g2499以猜到它g1262g5602么做:由于不
g11705g17959这g1022g4557象应该算是基类还是派生类的,g3252g8504动态绑定g1262在运行时进行
g16311析。出于一致性的考g15397,g1332g2499能g1262g16760g1038构g17908函数也应该这么作。
g1119g4466g5194g19762完全g3926g8504。g3926g7536g1332在构g17908函数g18336g19766调g11004了动态绑定的g7053法,那么它g1262g1363g11004那g1022g16218g1901后的版本。但是这g1022g256g6940g7536g257g1262有g1135出g1166g5859料,g3252g8504g1262
g6116g1038一g1135不易排查的bug的藏身之处。
从g10714论上g16774,构g17908函数的任务就是创建g4557象(这g2499不是什么轻g13792易举的
g1119)。从构g17908函数的角度来看,g4557象g2499能g2494创建了一半g252g252g1332g2494g11705g17959基类
g4557象g5062g13475初始g2282了,但是g1332还不g11705g17959它g1262派生出什么类。但是动态绑定的
g7053法调g11004,g1262从g256g3818g19766g257把g6175伸进g256类系(inheritance hierarchy)g257。
它调g11004的是派生类的g7053法。g3926g7536g1332在构g17908函数g18336g19766这么做的g16817,g1332就g2499能调g11004了一g1022g256g1262访问尚未初始g2282的g6116员g257的g7053法了g252g252这注定g1262出问题。
g1332g2499以从下g19766这g1022例g4388g10714g16311这g1022问题,
//,c07:PolyConstructors.java
// Constructors and polymorphism
// don't produce what you might expect,
import com.bruceeckel.simpletest.*;
abstract class Glyph {
abstract void draw();
Glyph() {
System.out.println("Glyph() before draw()");
draw();
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 {
private static Test monitor = new Test();
public static void main(String[] args) {
new RoundGlyph(5);
monitor.expect(new String[] {
"Glyph() before draw()",
"RoundGlyph.draw(),radius = 0",
"Glyph() after draw()",
Chapter 7,Polymorphism
g12544 22 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
"RoundGlyph.RoundGlyph(),radius = 5"
});
}
} ///:~
Glyph的draw( )g7053法是abstract的,所以这g12193设g16757就是要叫g2047g1166来
g16218g1901的。g4466g19481上,g1332g5529g20047在RoundGlyphg18336g16218g1901draw( )。Glyph
的构g17908函数调g11004了draw( ),g13792这g1022调g11004最后落到了
RoundGlyph.draw( ),看来一g2011g18129g8503g5132。但是当g1332查看g17767出的时候,g1332就g1262g2469g10628,Glyph的构g17908函数调g11004draw( )的时候,radius的
g1552还g8821有被设g6116g13582省的初始g15521。它还是0。g3926g7536是g11507的g13546g12255的g16817,这g2499
能g1262是屏幕上画的一g1022点,甚g14279是什么g18129不画,g13792g1332g2614,g1262在那g18336瞪g3835了眼睛,g17165g2159地排查到底那g18336出了g19181。
在上g19766g12468节介绍的初始g2282顺序g5194不完g6984,g13792g13582失的部分才是这g1022谜团的关键。g11507g8503的初始g2282过g12255是这g7691的,
1,在进行g1866它工作之前,分配g13485这g1022g4557象的内g4396g1262先被初始g2282g1038两进g2058的零。
2,g8503g3926前g19766一g11464在所说的,先调g11004基类的构g17908函数。这时g1262调g11004被g16218g1901的
draw( )g7053法(是的,在调g11004RoundGlyph的构g17908函数之前调g11004),这时它g2469g10628,由于受第一步的g5445g2721,radius的g1552还是零。
3,数据g6116员按g10043它们g3780g7138的顺序进行初始g2282。
4,调g11004派生类的构g17908函数的g8503g7003。
这g7691做有g1022g3921处,就是它不g1262留一堆垃圾,最起g11733把所有的东西g18129初始g2282
g1038零了(g7092论是g2750g12193类型的,g18129是零)。这g1866中也包括g11004合g6116g4896进去的g4557
象,这g1135referenceg18129被设g6116了null。所以g3926g7536g1332g5548了g4557reference进行初始g2282,运行的时候就g1262g6255出g5334g5132。所有的东西g18129是零,这g7691查看g17767出的时候多g4581g1262有点g13459g13046。
但是另一g7053g19766,g1332g2499能g1262g4557g12255序运行的结g7536g3835g2519一g5790。g1332的设g16757完g13666g7092
g13582,但是g12255序运行的结g7536就是那g7691g19181g5483离g16901,g13792且g13546译器还不g6265g19181。(在这g12193g5785g1929下,C++的g13546译器g1262有一g1135比较g10714性的反g7156。)这类bugg5468容易
g1262被g5585g11065,g13792且要g14469g5468长的时间g2469g10628。
结论就是,一g1022g3921的构g17908函数应该,g256g11004最g4581的工作g18339把g4557象的g10378态设g13634
g3921,g13792且要尽g2499能地避g1825去调g11004g7053法。g257构g17908函数唯一能安全调g11004的g7053
法,就是基类的finalg7053法。(这一g7477也g17878g11004privateg7053法,g3252g1038它g14270动就是final的。)它们不g1262被g16218g1901,g3252g8504也不g1262g1147生这g12193g5859g3818的行g1038。
用继承来进行设计
Thinking in Java 3rd Edition
g12544 23 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
一g7098g10714g16311了多态性,g1332就g1262觉g5483所有东西应该g18129是g13499承下来的,g3252g1038多态性g4466在是g3838g13886g7138了。但是这g7691做g1262g2164g18337设g16757的g17139g6297;g4466g19481上,g3926g7536g1332一g17947
到g256要g11004g5062有的类来创建新类g257的g5785g1929就想到要g11004g13499承的g16817,g1119g5785就g1262g8639
g7092g5529要地g2476g5483复杂起来了。
较g3921的办法还是先考g15397合g6116,特g2047是当g1332不g11705g17959该g13499承g2750g1022类的时候。合
g6116g5194不强求g1332把设g16757g6642g6116一g1022类系。g8504g3818它还g7368g9801g8975,g3252g1038g1363g11004合g6116的时候,g1332g2499以动态地选择g6116员的类型(以及它们的行g1038),g13792g1363g11004g13499承的g16817,
就g5483在g13546译时指g7138g4557象的g11842g2011类型。下g19766这段g12255序就演示了这一点,
//,c07:Transmogrify.java
// Dynamically changing the behavior of an object
// via composition (the "State" design pattern),
import com.bruceeckel.simpletest.*;
abstract class Actor {
public abstract void act();
}
class HappyActor extends Actor {
public void act() {
System.out.println("HappyActor");
}
}
class SadActor extends Actor {
public void act() {
System.out.println("SadActor");
}
}
class Stage {
private Actor actor = new HappyActor();
public void change() { actor = new SadActor(); }
public void performPlay() { actor.act(); }
}
public class Transmogrify {
private static Test monitor = new Test();
public static void main(String[] args) {
Stage stage = new Stage();
stage.performPlay();
stage.change();
stage.performPlay();
monitor.expect(new String[] {
"HappyActor",
"SadActor"
});
}
} ///:~
Stageg4557象包g2559了一g1022Actor的reference,g13792这g1022referenceg2460被初始g2282g1038HappyActor。也就是说performPlay( )g1262有一g1135特殊的
Chapter 7,Polymorphism
g12544 24 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
行g1038。但是g12255序运行时Actor的referenceg2499以g17842到另一g1022g4557象上,g3252
g8504g2499以g11004SadActorg4557象来g7379g6454它,于是performPlay( )的行g1038就g2469
生了g2476g2282。这g7691g1332就在运行时g14731g5483了高度的g9801g8975性。(这也被称g1038g256g10378态
g8181g5347(State Pattern)g257。参见www.BruceEckel.com 所g2014g17745的
Thinking in Pattern (Java版))。反g16278g13499承,它不能g16765g1332在运行时g13499承不g2528的类;这g1022问题在g13546译的时候就g5062g13475定下来了。
有一g7477一g14336g1946则g256g1363g11004g13499承来表示行g1038的不g2528,g13792g11004g6116员数据来表示不g2528
的g10378态。g257上g17860例g12255g2528时g1319g10628这两g13785;两g1022派生类g11004来表示act( )g7053法的不g2528,g13792Stage则g1363g11004合g6116来表示g10378态的g2476g2282。在这g12193g5785g1929下,g10378态的不g2528g1262g4560致行g1038的不g2528。
纯继承与扩展
看来,g11752g12362g13499承的最g3921g7053g5347,还是g11004g256纯g13499承g257的g7053g5347创建一g1022类系。也就是说,派生类仅g16218g1901基类或interfaceg18336有的g7053法。就像下g19766这g5364
图,
由于类是由g1866接口所决定的,g3252g8504这g12193关系被称g1038纯的g256是g257关系。g13499承保证了所有派生类g18129g14279g4581g6329有基类的接口。g13792g3926g7536g1332采g13447了这g5364图,那么
g13499承类的接口就不g1262比基类的g7368g3835。
g2499以把这想像g6116g256完全g7379代(pure substitution)g257,g3252g1038完全g2499以g11004派生类的g4557象来g7379g6454基类的g4557象,g13792且g1332这g7691做的时候g7693本不g19668要任g1321g4388类的g1461g5699,
Thinking in Java 3rd Edition
g12544 25 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
也就是说,由于有着相g2528的接口,基类g2499以接受任g1321g2469g17877g13485派生类的g9052
g5699。 g1332所要做的,g2494是将派生类的g4557象上传,g9994后就不g1889g19668要g11705g17959这g1022
g4557象是什么类型的了。所有的东西g18129g1144由多态性去处g10714。
看到这g18336,g1332g1262觉g5483纯的g256是g257关系才是唯一合g10714的g7053g7708,g13792g1866它g7053g7708g18129
不过是一g1135g5617g17347g9163g1093,g5194且前后g11695g11474的东西。这也是一g1022g16835g2318。g3926g7536g1332g11004
这g12193g5617g17347考g15397问题的g16817,g5468g5567就g1262g2469g10628g1119g4466g5194g19762g3926g8504,g4557g7588g1135问题g13792言,
扩展接口(看到了g2539,extends关键g16801就是在鼓励g1332去这么做)是一g1022完
g13666的g16311决g7053g7708。这g12193关系g2499以g11004g256像是(is-like-a)g257这g1022g7427g16833来表示,g3252
g1038派生类g256像g257基类g252g252它有着相g2528的基本接口g252g252但是它还有一g1135g1866它特性,g19668要g4466g10628一g1135g20081g3818的g7053法,
g15441g9994这也是一g12193有g11004g5194且合g10714的g7053g5347(g7693据g5785g1929),但是它有一g1022g13582点。不能通过基类访问派生类的扩展接口,所以上传之后,g1332就g7092法调g11004新的g7053
法了,
g3926g7536g1332不g1363g11004上传,那么什么问题g18129g8821有。但是通g5132g5785g1929下,g1332g18129g1262g11908到
g256要g18337新g2469g10628这g1022g4557象的g11842g2011类型g257的g5785g1929,这g7691g1332才能g1363g11004那g12193类型的扩展g7053法。下g19766一节g1262告诉g1332该g5602么做。
下传与运行时的类型鉴别
Chapter 7,Polymorphism
g12544 26 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
由于g256上传upcastg257(g8851着g13499承关系向上g12239)之后,类的g1867g1319g1461g5699g1014了,
g3252g8504g256g11004『下传(downcast)』g252g252也就是g8851着g13499承关系g18337新向下g12239g252g252
来提取类型的g1461g5699g257就g6116了顺g10714g6116g12468的g1119了。g1332g11705g17959上传总是安全的;基类的接口不g2499能比派生类的g7368g3835。g3252g8504它g13955定能收到那g1135通过基类接口g2469
g17877的g9052g5699。但是g11908到下传的时候,g1332就不能g13955定g256这g1022g5430g10378是不是g3290了g257
(g2494是举g1022例g4388)。它g2499以是一g1022三角型,一g1022g11709g5430或是g1866它什么g5430g10378。
要想g16311决这g1022问题,g5529g20047要有办法能g3827g11842保下传是g8503g11842的,这g7691g1332就不g1262
把g4557象g16835传g13485另一g1022类型了,于是也不g1262向它g2469g17877什么它不能接受的g9052g5699
了。这是相当不安全的。
g7588g1135g16833言(像C++)g19668要g13475过g1363g11004特g2047处g10714,才能安全地进行下传。但是
Java类型传递g18129要g13475过g7828查g701所以,尽g12661看上去g2494是g11004一g4557括g2507作了g1135
g7234通的的类型g17728g6454,但是运行的时候,系g13491g1262g4557这g1135g17728g6454作g7828查,以g11842保它g11842g4466是g1332想要g17728g6454的类型。g3926g7536不是,g1332就g1262g5483到一g1022
ClassCastException。这g12193运行时的类型g7828查被称g1038g256运行时的类型
g18504g2047(run-time type identificationg13565g1901g1038RTTI)g257。下g19766的例g12255演示了RTTI的行g1038,
//,c07:RTTI.java
// Downcasting & Run-Time Type Identification (RTTI),
// {ThrowsException}
class Useful {
public void f() {}
public void g() {}
}
class MoreUseful extends Useful {
public void f() {}
public void g() {}
public void u() {}
public void v() {}
public void w() {}
Thinking in Java 3rd Edition
g12544 27 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
}
public class RTTI {
public static void main(String[] args) {
Useful[] x = {
new Useful(),
new MoreUseful()
};
x[0].f();
x[1].g();
// Compile time,method not found in Useful,
//! x[1].u();
((MoreUseful)x[1]).u(); // Downcast/RTTI
((MoreUseful)x[0]).u(); // Exception thrown
}
} ///:~
g8503g3926图表所示,MoreUseful扩展了Useful的接口。但是由于它是g13499
承的,g3252g8504g2499以将它上传g13485Useful。g2499以看到,当 main( )g4557数g13464x
进行初始g2282的时候,就进行了上传。由于数g13464中的两g1022g4557象g18129是Useful
的,g3252g8504g1332g2499以向它们g2469g17877f( )和g( )g9052g5699,但是g3926g7536g1332调g11004了
u( )(g2494有MoreUseful才有),g13546译的时候就g1262g6265g19181。
g3926g7536g1332想访问MoreUsefulg4557象的扩展接口,g1332就g5483先下传。g3926g7536类型
g8503g11842,这g1022操作就g1262g6116功。否则,g1332就g1262g5483到ClassCastException。
g1332不g5529g1038这g1022g5334g5132g13546g1901什么特殊的代g11733,g3252g1038它表示这是g12255序员g10371的g19181
g16835,g13792这g12193g19181g16835g2499能g2469生在g12255序的任g5859地g7053。
RTTI要比g11464接g17728g6454g7368复杂。举例来说,g1332g2499以在下传之前先看一看g256这
g1022g4557象是g2750g12193类型的g257。我们g1262在第10g12468,g11004g6984g12468的g12699g5145g11752g12362Java的运行时类型g18504g2047。
总结
多态性的g5859g5617是g256不g2528的g5430g5347g257。在g19766向g4557象的g13546g12255中,g1332g1262有g256一g5364相
g2528的g14092g257(基类的公g1861接口)和g5468多g256不g2528的g1363g11004这g5364g14092的g7053g5347g257:各g1022版本的动态绑定g7053法。
g1332在本g12468也看到了,g3926g7536不g10714g16311数据抽象和g13499承的g16817,是g7693本不g2499能g10714g16311
多态性的,g7368不g11004说创建多态性的例g4388了。多态性是一g12193不能g4408g12447的看g5465
的特性(不像switchg16833g2489),相反g2494有放在类关系的g256g3835g13984g7235g257下,它才有g11004g8506之地。g1166们通g5132g1262把它g2528Java的那g1135g19762g19766向g4557象的特性相g9163g9114,
比g3926g7053法的g18337g17745,它g5132g5132g1262被当作g19766向g4557象的特性介绍g13485g3835g4490。g2327万g2047上当:不是后绑定的,就不是多态性。
Chapter 7,Polymorphism
g12544 28 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
要想在g13546g12255中有g6940地g1363g11004多态性,以及g19766向g4557象的g6228g7427,那g1332就g5529g20047扩展
g1332的g13546g12255g16282g18338,不能g2494关注单g1022类的数据g6116员和g9052g5699,g13792是要去g10714g16311类g994
类之间的g1861g2528性,以及它们之间的关系。g15441g9994这g1022要求g5468高,但是这g12193g2174
g2159是g1552g5483的,g3252g1038它能g2164g17907g12255序的开g2469,g6925善代g11733的逻辑g13464g13467,g1363g5483g12255序
g7368易于扩展,g2528时g13512g6264代g11733也g2476g5483g7368g7053便了。
练习
g2494要g1196g5468g4579一g12520g17165g11004就能从www.BruceEckel.com下g17745g2529g1038The
Thinking in Java Annotated Solution Guide的g11017g4388g7003g7735,这上g19766有一g1135g1076题的g12584g7708。
1,g1038Shapes.java的基类g2164一g1022能打印g9052g5699的g7053法,但是g2047在派生类g18336
g16218g1901这g1022g7053法。预测一下g1262有什么结g7536。g10628在,g1889在一g1022派生类g18336g16218g1901
这g1022类,但是g2047在g1866它派生类g18336g16218g1901,看看结g7536g1262g5602g7691。最后,在所有的派生类g18336g16218g1901这g1022g7053法。
2,向Shape.java添g2164一g1022新的类,g9994后g11004main( )g7828查一下,看看是不是像和g7099类一g7691,多态性在新类上也一g7691能g8503g5132工作。
3,g1474g6925Music3.java,g16765what( )g6116g1038Object的toString( )g7053
法。试着g11004System.out.println( )打印Instrumentg4557象(不要g11004
类型g17728g6454)。
4,g5460Music3.javag18336g19766g2164一g12193新的Instrument,看看多态性是不是也g1262g4557新类型g8503g5132工作。
5,g1474g6925Music3.java,g16765它以Shapes.java的g7053g5347g19555g7438创建
Instrumentg4557象。
6,创建一g1022Rodent(g2882g21843动g10301)类系:Mouse,Gerbil,Hamster,
等等。在基类g18336定g1053所有Rodent所g1861有的g7053法,g9994后在派生类g18336g7693据
Rodent的g1867g1319类型g16218g1901这g1135g7053法,以提供不g2528的行g1038。创建一g1022
Rodent的数g13464,g11004各g12193g1867g1319的Rodentg3647g9397这g1022数g13464,g9994后调g11004基类的g7053法,看看g12255序运行的结g7536。
7,g1474g6925g13463g10766,将Rodentg6925g1901g6116abstract的类。g2494要有g2499能,就把
Rodent的g7053法作g6116抽象g7053法。
8,创建一g1022不g2559任g1321abstractg7053法的abstract类,g9994后g20576证一下,g1332
是不是不能创建这g1022类的g4466例。
9,g5460Sandwich.javag18336g19766添g2164Pickle(g18252g21656g10928)类。
10,g1474g6925g13463g10766,g11004它来演示基类g994派生类的初始g2282顺序。g1889g5460基类和派生类g18336g2528时g2164入g6116员g4557象,g9994后看看它们在创建过g12255中的初始g2282顺序。
11,创建一g1022有两g1022g7053法的基类。g11004第一g1022g7053法调g11004第二g1022g7053法。g13499承这g1022
类,g5194g16218g1901第二g1022g7053法。创建第二g1022类的g4557象,g5194上传到g1866基类,g9994后调g11004第一g1022g7053法。说说看,g1262有什么结g7536。
12,创建一g1022带abstract print( )g7053法的基类,g9994后在派生类g18336g16218g1901这
g1022g7053法。g16218g1901后的g7053法要能打印在派生类g18336定g1053的intg2476g18339的g1552。定g1053
这g1022g2476g18339的时候,g17183g13485它一g1022g19762零的g1552。在基类的构g17908函数g18336,调g11004这
Thinking in Java 3rd Edition
g12544 29 g20041 g1861 29 g20041 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
g1022g7053法。g11004main( )创建一g1022派生类的g4557象,g9994后调g11004print( )g7053
法。g16311g18334一下g1038什么g1262有这g1022结g7536。
13,g7693据Transmogrify.java创建一g1022Starship类,这g1022类g18336要包g2559
一g1022AlertStatus的reference,g13792这g1022AlertStatus要能表示三
g12193不g2528的g10378态。g1901一g1022g2011g6454g10378态的g7053法。
14,创建一g1022不带g7053法的abstract类。g13499承这g1022类,g5194且添g2164一g1022g7053
法。g1889创建一g1022g1262将基类的reference下传到派生类,g5194且调g11004这g1022g7053
法的staticg7053法。g11004main( )g7828g20576一下,看看是不是能g8503g5132工作。g9994
后在基类g18336把这g1022g7053法g3780g7138g6116abstract的,这g7691就不g19668要下传了。
[32]g4557于C++的g12255序员来说,这就是C++的g256纯g15406函数(pure virtual
function)g257。