Thinking in Java 3 Edition Abstract or Concrete interface 1 Base d ass interface erface n Base Class Methods interface 1 interface 2.. interface n ava并不强制你一定要去继承 abstract还是“具体”的类(就是不带 abstract方法的类),但是你只能继承一个非 interface的类。所有别 的基类元素( base elements)都必须是 interface。你得把所有的接口 名字都放在 implements关键词后面,用逗号把它们分开。你可以根据 需要,实现任意多个 interface;也可以将这个类上传至任何一个 interface。下面这段程序演示了如何将一个具体的类同几个 interface合并起来,创建一个新的类 Multiple interfaces interface CanFight void fight ( interface CanSwim interface CanFly void fly( class Actioncharacter public void fight()() class hero extends Actioncharacter implements CanFight, CanSwim, CanFly t public void swim()() public void fly()i public class Adventure t public static void t(CanFight x)(x fight ()i public static void u(CanSwim x)i public static void v(CanFly x)(x fly(i 1 public static void w(Actioncharacter x) ght()i J public static void main(String[] args)t ero lew Hero t(h)i// Treat it as a CanFight u(h)i// Treat it as a Canswim 第6页共47页 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
Thinking in Java 3 rd Edition www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com ✄ 6 ☎ ✆ 47 ☎ Java X_¡3O%ö8 abstract ͺ»Ï((X¬ abstract µ()¡ó;ö83ׯ in terface (Dm¨ (B¼½(base el em ents)Æ in terface¡PDm( ѾÆkF impl emen ts -³¶¿ÀPA¡ÝE;m î%ñít¹V× in terface [ÝE ×JAÁt3× in terface¶ Dâ%,Ä3׺»(i× in terface °9Q123×Â(L //: c08:Adventure.java // Multiple interfaces. interface CanFight { void fight(); } interface CanSwim { void swim(); } interface CanFly { void fly(); } class ActionCharacter { public void fight() {} } class Hero extends ActionCharacter implements CanFight, CanSwim, CanFly { public void swim() {} public void fly() {} } public class Adventure { public static void t(CanFight x) { x.fight(); } public static void u(CanSwim x) { x.swim(); } public static void v(CanFly x) { x.fly(); } public static void w(ActionCharacter x) { x.fight(); } public static void main(String[] args) { Hero h = new Hero(); t(h); // Treat it as a CanFight u(h); // Treat it as a CanSwim
Thinking in Java 3 Edition v(h); Treat it as a canFly w(h); Treat it as an Actioncharacter 可以看到,Hero合并了具体的 Action character类,以及 Can Fight, Can swin和 Can Fly接口。当你用这种方式合并实体类 ( concrete class)和接口的时候,必须将实体类放在前面,然后才是接 口。(否则编译器就会报错。) 注意 Action character类的 fight()方法。它的特征与 interface Can Fight的 fight()方法完全相同,但是Hero没有提供fght()的 定义。 interface的规则是这样的,你可以继承它(马上就会说到),但 是继承下来的还是 interface。如果你想创建一个新类型的对象,那么 这个类型就必须是类,而且还得提供所有定义。尽管Hero没有明确的提 供 fight()的定义,但是 Action character提供了,所以Hero能自 动获得这个方法,并且能创建对象了 Adventure类有四个拿接口和实体类作参数的方法。创建出来的Hero 对象可以被传给其中任何一个方法,也就是说它被依次上传给了各个 interface。这个过程不需要程序员编写特别的代码,这一切要归功于接 口在Java中的设计。 上述程序告诉我们接口的真正目的:能够上传到多个基本类型(base type)。然而,使用接口的第二个理由,实际上是和“把 abstract类用 做基类”完全相同的:就是要禁止客户程序员去创建这个类的对象,并且 重申“这只是一个接口”。这就带来了一个问题:到底是用 interface,还是用 abstract类? interface既给了你 abstract类 的好处,又给了你 interface的好处,因此只要基类的设计里面可以不 包括方法和成员变量的定义,你就应该优先使用 interface。实际上, 如果你知道这样东西可能会是基类的话,你就应该优先考虑把它做成 interface,只有在不得不定义方法或成员变量的情况下,你才能把它改 成 abstract类,或者根据需要改成实体类。 合并接口时的名字冲突 实现多个接口的时候可能会遇到一些小问题。在上述例程中, Can Fight 和 Action character都有一个一摸一样的 void fight()方法。这里 没有问题,因为它们使用的是同一个方法。但是如果不是呢?下面就是 个例子: //: c08: Interfacecollision. java interface Il void f(i interface I2 int f(int i)i] 第7页共47页 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
Thinking in Java 3 rd Edition ✄ 7 ☎ ✆ 47 ☎ www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com v(h); // Treat it as a CanFly w(h); // Treat it as an ActionCharacter } } ///:~ ÝE<gH ero °,º»( Action Character Eà Can F igh tCan Swim Can Fl y ¡ µ´°ñ» (concrete cl ass)(M ñ»kF¶Z (Ä9áºÅÆ) ¹ Action Character ( figh t( )µA()Ç in terface Can F igh t ( figh t( )µc> H ero m figh t( )( O4in terface (È9 ¦(¡ÝEö8A(ÉJº$g ) ö8Q( in terfaceÄÅ¡|123×Âh(¥!YZ ×h à³DmO4ÊË H ero m$Ì( figh t( )(O4 Action Character ,DE H ero ;ý Í ×µ³;12¥!, Adven tu re mÎ×Ïñ»1c(µ12!Q( H ero ¥!ÝEvA¬§*t3×µ[$AvШJA¬,Ñ× in terface ×^âXî%â%&á«)¨(K 3Ò%ÓF) F Java *(+, Jâ%#(zj«(L;ÔJAgV×B h(base type)àç(×¢7ñ¢JÍP abstract TBÏc>(L%Õ+Ö×â%&12 ×(¥!³ ØÍ ó3×Ï ¬Q,3×¹Lgh in terface abstract Ê in terface ²¬,¡ abstract (Ùü¬,¡ in terface (Ùæòó%B(+,˶ÝEX kÚµ&Û(O4¡vwV/ç in terfaceñ¢J ÄÅ¡NO ¦ÜÝÝ;ºB(¡vwV/ÞPAT in terfaceómFXXO4µ3&Û(´¡Z;PAð abstract 3;mî%ðñ» ñíV×(MÝ;º±g3Ìß¹FJâ*Can F igh t Action Character Æm3×3à3¦( void figh t( )µ Ë m¹æAç(3×µÄÅXáÊ ¶3 ×L //: c08:InterfaceCollision.java interface I1 { void f(); } interface I2 { int f(int i); }
Thinking in Java 3 Edition interface I3 int f()i) class ct public int f() return li 1 class C2 implements Il, I2 public void f()I public int f(int i)i return l;// overloaded class C3 extends c implements I2 public int f(int i) return l; ) / overloaded } class C4 extends c implements I3 / Identical, no probl public int f() return 1 / Methods differ only by return type //! class C5 extends c implements I1 t // interface I4 extends Il, I3///: N 这个难题要归因于覆写、实现和重载的不期而遇,以及“不能仅通过返回 值来辨别重载的方法”。如果把最后两行的注释去掉,就会出现如下的错 误信息 Interface Collision java: 23: f() in C cannot implement f() in Ii; attempting to use incompatible return type found: int required: void Interface Collision java: 24: interfaces 13 and I1 are incompatible; both define f( but with different return type 而且在要合并的接口里面放上同名方法,通常也会破坏程序的可读性。所 以别这么做 用继承扩展 interface 你可以用继承,往 interface里面添加新的方法,也可以用继承把多个 interface合并成一个新的 interface。在这两种情况下,你所得到的 都只是一个新的 interface,就像下面这样: //: c08: Horrorshow. java / Extending an interface with inheritance nterface Monster i interface DangerousMonster extends Monster i void destroy()i 第8页共47页 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
Thinking in Java 3 rd Edition www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com ✄ 8 ☎ ✆ 47 ☎ interface I3 { int f(); } class C { public int f() { return 1; } } class C2 implements I1, I2 { public void f() {} public int f(int i) { return 1; } // overloaded } class C3 extends C implements I2 { public int f(int i) { return 1; } // overloaded } class C4 extends C implements I3 { // Identical, no problem: public int f() { return 1; } } // Methods differ only by return type: //! class C5 extends C implements I1 {} //! interface I4 extends I1, I3 {} ///:~ ×U¹%Óæ)â«ã ñíä(X@à±EÃÍX;<^ef gQå¨ä(µÏÄÅPbe}(渺!íÄ(Æ çèéL InterfaceCollision. java: 23: f( ) in C cannot implement f( ) in I1 ; attempting to use incompatible return type found : int required: void InterfaceCollision. java: 24: interfaces I3 and I1 are incompatible; both define f( ), but with different return type à³F%°(˶kJѵ·[ºêâ%(Ý*D E¨ ZT i n terface ¡ÝEö8ë in terface ˶ìäÂ(µ[ÝEö8PV× in terface °3×Â( in terfaceF e´¡Dg( Æó3×Â( interface}¶ ¦L //: c08:HorrorShow.java // Extending an interface with inheritance. interface Monster { void menace(); } interface DangerousMonster extends Monster { void destroy(); }
Thinking in Java 3 Edition terface lethal void kill( class DragonZilla implements DangerousMonster i public void menace()i) public void destroy()i] interface Vampire extends DangerousMonster, Letha void drinkBlood ( class VeryBadVampire implements Vampire t public void menace() public void destroy()IH public void kill()() public void drinkBlood()[] public class HorrorShow i static void u(Monster b)i b. menace(i static void v(DangerousMonster d)i d ddestroy()i static w( Letha11){1.ki11(); public c void main(string[ args) t Dange monster barney new DragonZilla()i u(barney)i v(barney) Vampire vlad new VeryBadVampire( u(vlad)i v(vlad) w(vlad)i DangerousMonster只是对 Monster做了一点扩展,然后生成一个 新的 interface。 Dragonzilla则实现了这个接口 Vampire的语法是“接口继承( inheriting interfaces)”所独有的。通 常情况下, extends只能用于类,但是由于一个 interface可以由多 个接口拼接而成,因此创建新的 interface的时候可以用 extends来 表示其多个“基接口( base interfaces)”。正如你所看到的 interface的名字要由逗号分隔 常量的分组 由于 interface的数据成员自动就是 static和 final的,因此 interface是一种非常方便的,创建一组常量值的工具。这点同C和 C++的enum很相似。例如: 第9页共47页 www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com
Thinking in Java 3 rd Edition ✄ 9 ☎ ✆ 47 ☎ www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com interface Lethal { void kill(); } class DragonZilla implements DangerousMonster { public void menace() {} public void destroy() {} } interface Vampire extends DangerousMonster, Lethal { void drinkBlood(); } class VeryBadVampire implements Vampire { public void menace() {} public void destroy() {} public void kill() {} public void drinkBlood() {} } public class HorrorShow { static void u(Monster b) { b.menace(); } static void v(DangerousMonster d) { d.menace(); d.destroy(); } static void w(Lethal l) { l.kill(); } public static void main(String[] args) { DangerousMonster barney = new DragonZilla(); u(barney); v(barney); Vampire vlad = new VeryBadVampire(); u(vlad); v(vlad); w(vlad); } } ///:~ Dan gerou sM on ster ó¥ M on ster T,3Cíî03× Â( in terfaceDragon Zil la 9ñí, × Vampire (Íö8(inh eriting interfaces)ÏDïm( ·´ exten ds ó;)7)3× in terface ÝE7V ×ðàæò12Â( in terface (MÝE exten ds Q ?§V×ÍB (base interfaces)ÏjÄ¡D<g( in terface (Ѿ%7¿Àñ 7) in terface (cm&ý static final(æò in terface 3¯·µ(123·Ûg(yº C C C+ + ( en um >EÄL
Thinking in Java 3 Edition //: c08: Months. java // Using interfaces to create groups of constant package c08; public interface Months i JANUARY = 1, FEBRUARY =2, MARCH =3, APRIL 4, MAY = 5, JUNE JULY AUGUST =8, SEPTEMBER =9, OCTOBER =10 NOVEMBER =11, DECEMBER =12; }///: 注意一下,Java的编程风格是,用全部大写字母(用下划线分隔同一个标 识符里的各个单词)来表示,用常量进行初始化的 static fina变量 nterface的数据成员自动就是 public的,因此就不必再注明了。 你可以像对待别的 package那样,用 import c08*或者 c08 Months把它引进来,这样就能在这个 package的外面用 Months JANUARY之类的表达式来使用这些常量了。当然,你得到的 是一个int,因此它没有像C++的enum那样的类型安全,但是这种 (很常见的)手法要比直接在程序里面用数字要好得多。(这种方法通常被 成为使用“神奇数字”,并且使得代码的维护变得非常困难。) 如果你确实需要额外的类型安全,可以像这样创建一个类:③3 // A more robust enumeration system public final class Month t t() private string name; private Month(String nm) name nm; 1 public string tostring( return name public static final Month JAN new Month("January ") FEB new Month("February") MAR new Month("March"), nth(Al AY new Month("May") JUN new Month("June " JUL Month("July " AUG new Month("August " SEP new Month("September") OCT new Month("October") NOV nth("November") DEC new Month("December )i public static final Month[] month = t JAN, FEB, MAR, APR, MAY, JUN, 第10页共47页 www.wgqqh.com/shhgs/tij.html
Thinking in Java 3 rd Edition www.wgqqh.com/shhgs/tij.html email:shhgs@sohu.com ✄ 10 ☎ ✆ 47 ☎ //: c08:Months.java // Using interfaces to create groups of constants. package c08; public interface Months { int JANUARY = 1, FEBRUARY = 2, MARCH = 3, APRIL = 4, MAY = 5, JUNE = 6, JULY = 7, AUGUST = 8, SEPTEMBER = 9, OCTOBER = 10, NOVEMBER = 11, DECEMBER = 12; } ///:~ ¹3Java (áâòèc«¾ó(ôõñ3×ö ÷øË(Ñ×H³ )Q?·Û]}f`( static finalÛ interface public ¡ÝE}¥ù¨( package Y¦ import c08. * 3 c08. M on th s PAú]Q ¦;F × package (½¶ M on th s. JAN UARY u(?û´Qç Ì·Û, ¡g( 3× in tæòAm} C+ + ( en um Y¦(hüc (· ()C%8ÒFâ%˶c¾%V( µ·v çÍýþc¾Ï³çK(¯·U) ÄÅ¡Ìñî%½(hücÝE} ¦123×L ✝ ✞ ✞ ✟ //: c08:Month.java // A more robust enumeration system. package c08; import com.bruceeckel.simpletest.*; public final class Month { private static Test monitor = new Test(); private String name; private Month(String nm) { name = nm; } public String toString() { return name; } public static final Month JAN = new Month("January"), FEB = new Month("February"), MAR = new Month("March"), APR = new Month("April"), MAY = new Month("May"), JUN = new Month("June"), JUL = new Month("July"), AUG = new Month("August"), SEP = new Month("September"), OCT = new Month("October"), NOV = new Month("November"), DEC = new Month("December"); public static final Month[] month = { JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV, DEC };