性格决定命运 气度左右格局 拼搏方能取胜 谦虚才会进步

轻松掌握 Java 泛型 (第 4 部分)

上一篇 / 下一篇  2008-01-10 18:04:06 / 个人分类:做着

轻松掌握 Java 泛型 (第 4 部分)ITPUB个人空间j+W8rb,H
来源:** 收集整理

 ITPUB个人空间6j4fA;~XK
 
X9Id.j;[7H@2F/{e0 ITPUB个人空间-?6A*p;Xk r
 
^`7nD?#J Es0 
m:J]"b]3bw Y0  Java 开发人员和研究ITPUB个人空间7k;N'SiiV
的影响,并以此文来结束他ITPUB个人空间KZ|/l|He
在相关论坛中与作者及其他
_[Jp+Pn5o)M0论”来访问该论坛。)至此
4b+z-X8s~@S L[&F0已经探讨了:ITPUB个人空间$F S kK2wT
 人员 Eric Allen 讨论了通过泛ITPUB个人空间O/d-Up gx'F
对 JSR-14 和 Tiger 中泛型类
0}.q8Y [n0VQc!]|)[0读者分享您对本文的看法。(您ITPUB个人空间6k0e;Zhz+ex j5u
,在这个讨论 JSR-14 和 Tiger

 型类型添加对 mixin 支持所带来ITPUB个人空间wLp"y\+x%_Y
型的由四部分组成的系列的讨论。ITPUB个人空间 [E:Zpv!nXr!i
也可以单击本文顶部或底部的“讨
sg3`Z(E0中泛型类型的微型系列中,我们

 

  泛型类型及被设计成支持它们的即将发布的功能                                    

  基本类型、受约束的泛型以及多态方法上的限制                                    

  几个强加给这些 Java 扩展的限制                                          

  这些扩展语言的编译器所用的实现策略如何使这些限制成为必需                      

  在泛型类型中添加对“裸”类型参数的 new 操作的支持所带来的影响            

  本月,我们将探讨在可ITPUB个人空间!a:s'W6E0p&L6M
先解决的问题,以此来结束ITPUB个人空间#V1M1QWS
 以处理 mixin(可能被期望是泛
(o nZnC,q0对 Java 语言中泛型类型的讨论ITPUB个人空间9q'n{3u:g(s5P Eh
 型类型中最强大的功能)之前需要
9AM!mr5C'I8`$DZP0ITPUB个人空间ca%}&sD x;H
 

  mixin vs 包装                                                        ITPUB个人空间2C-g1L0r6yH-VC
  mixin 是由其父类参数ITPUB个人空间k!g0V"K]&Tq4}l
数:ITPUB个人空间+@ WX9It$p
 化的类。例如,请考虑以下这个

 泛型类,它继承了它本身的类型参

 

  class Scrollable< T> extends T {...}


^4f9jF5Z$bvX0  不要错过本系列的其它文章                                                      

  第 1 部分,轻松掌握 Java 泛型(2003 年 2 月)               

  第 2 部分,轻松掌握 Java 泛型类ITPUB个人空间B)~8Y,m9A,vsE
 型,第 2 部分(2003 年 3 月)
nnu&v%}j U*f*STf0 

  第 3 部分,轻松掌握 Java 泛型,第 3 部分(2003 年 4 月) 

  类 Scrollable 的目的是要向 GUI 窗口小部件嵌入添加可滚动性所必需的功能性。这个泛型类的每个应用都会继承一个不同的父类。例如,Scrollable< JTextPane> 是 JTextPane 的子类,而 Scrollable< JEditorPane> 是 JEditorPane 的子类。对比这种嵌入功能的方法和 Java Swing 库中现有的功能性,在这个库中,如果我们想使 JComponent 是可滚动的,必须将其“包装”在 JScrollPane 中。


muWY r,}0  包装不仅需要添加访问被包装类的功ITPUB个人空间!G M6j Zi(?i'J
实例的上下文中使用由此产生的可滚动ITPUB个人空间v"h9L6Hx,I
JTextPane 的实例的方法中)。通过 ScrITPUB个人空间0C#n6q%kSV[/s
们就能保持对涉及滚动的功能的单点控制
/kj&q)A3o]nR*w3^0的某些强大功能,而又没有附带异常。ITPUB个人空间9w9T DkbR5y `&u(hP\
 能的转发方法,而且它还阻止我们在需要被包装对象
hM1g2Dgew @O0对象(例如,我们不能将 JScrollPane 传递到需要
Q&L-k {Z/apJ8n%|j7[0ollable 的父类将其参数化,在继承多个超类时,我ITPUB个人空间#aN5mP3? `%k
。这样能够使用 mixin 让我们重新获得多重继承性

 

  在上面的示例中,我们ITPUB个人空间0b{ FD9pEf
如,我们可能想使该类型参
}$Vt#l(Rc[^0 甚至可以对类型参数施加约束以ITPUB个人空间;y YP2A+iP
数强制为 JComponent 的子类:ITPUB个人空间d P~S7p6T
 阻止它用于不适当的上下文中。例

 

  class Scrollable< T extends JComponent> extends T {...}

ITPUB个人空间?+Yw)\ns;D:{ Z(r&X6U
  那么我们的 mixin 只能继承 GUI 组件。                              

  mixin 和泛型类:完美组合                                               
!aH*{.NZ$c:J0  通常,mixin 作为独立语言功能部件ITPUB个人空间Z'j {1P }f K v
mixin 以作为泛型类型系统的一部分很吸ITPUB个人空间e"S q#yX~
类都能被认为是将现有类映射到新类的函
d!P L3w&o:i1o0 添加到某种语言中,就象 Jam 中的那样。但是合并
!X(P\t f([U_;y0引人,几乎可以说魅力无穷。原因是:mixin 和泛型
9G3lE,MC'r[0数。
Ml3S ^xt5w9vc!I0 

  泛型类可被视为将它们ITPUB个人空间H5o.v$W` G%W"e
子类的函数。通过使用泛型ITPUB个人空间adQEE{6t
 的参数映射成新实例化的函数。
&?[(Wqbc|;l.R0类型合并 mixin,我们能解决其ITPUB个人空间zig,P)Pk*A8~q
 mixin 可被视为将现有类映射成新ITPUB个人空间Q.M&Z4jF0`C6eG2m
它 mixin 公式的许多关键限制。ITPUB个人空间I/OI LNW
 

  在 Java 语言的 Jam
'|"M4g)wzi0中引用它。这一限制会迅速ITPUB个人空间LH+\j5t7B]
this 作为参数传递给方法ITPUB个人空间2u#Kn*[%m[
多最常见的设计模式都要依
-_3jw}@%k C0 扩展中,mixin 的超类类型没有
d,g!HY"lQ0引起一连串各种其它问题。例如
N._-o&Y E&Y M iR(m0;无法对这样的调用进行类型检
+T9WV'c/N_3\5{0赖于能够将 this 作为参数传递ITPUB个人空间~2w6]+G3hCP6y
 名称;我们就不能在 mixin 主体
j6y@f2i}0,在 Jam 中,禁止程序员将
B:@6d|q\0查。这一限制的影响极大,因为许
F#C!{G5vX0ITPUB个人空间:sEsf/h"L7`
 

  请考虑访问者模式,其
(SHi6t"aO'Qd0常被访问的类包含 accept
W~'@y3?%x-Z']Sc0在 Jam 中,访问者模式不
:{5s2do'D&aA`w0 中用 for 方法为复合层次结构ITPUB个人空间`*LFel#Z,O4r/h
方法,它采用访问者并传递 thi
Wn)EV il0能和 mixin 一起使用。ITPUB个人空间gg-F[2ZO
 中的每个类都定义了访问者类。通
}OV lm[0vM(b8u%G0s 来调用该访问者的方法。因此,

 

  将 mixin 明确表述为泛型类,我们
c:E"Z9E m4`)h8S-s&y\0如,我们可以将 Scrollable 的父类引用ITPUB个人空间._&K{&v;NHZ3X
传递时没有任何根本性的困难。
s.H+pPH+J0 就始终有父类的句柄,它是该类继承的类型参数。例ITPUB个人空间!SO"K&I{Q.\
为类型 T。其结果是,在允许将 this 作为类型参数

 

  但是,将 mixin 明确表述为泛型类
/]5r]-} [ S/`0产生的某些困难,我们将讨论几个突出的
!oE?DiV)u2UZk0 型时有其它一些明显的困难。为了让您初步体会可能ITPUB个人空间;ZbX B?/Fi~
困难以及一些可能的解决方案。
Ul&w[0`+^?.}0 

  mixin 与类型消除                                                        
FWRY8PT4Xw6|I_0  在讨论任何其它问题之ITPUB个人空间v`X.w,D
过使用由 JSR-14 和 Tiger
V!kk2b\ ZgV9F5V0支持添加到 Java 语言中。
por%KB%LV.h0 前,我们应该先指出,与上月讨
&I#R:ytxuH$|0使用的简单类型消除(type er

 论的泛型类型的功能扩展一样,通
c!@@*vH0asure)策略,不能将对 mixin 的

 

  要了解其原因,请考虑
m-S ZDIg%S{Y0型参数的界限!例如,上一ITPUB个人空间5\0z.l9^bv1}
。那显然不是我们所希望的
{A#F~8q"G @;Q0 在继承类型参数的类被消除时会ITPUB个人空间'~%Q9Ip D O1_
个示例中类 Scrollable 的每个ITPUB个人空间2oOc1s4Ji h

r(|:z an3hBj0 出现什么情况。该类会最终继承类ITPUB个人空间*h*[~T[O b
实例化最终都继承类 JComponent

 

  为了通过泛型类型支持 mixin,我们ITPUB个人空间r0c9` zvdjs
编码这一信息的方法有许多,它们实际上
&Fxo5c/uR5k0o.a0泛型 Java(Generic Java)的 NextGen
P k'J8s.~ q{0 需要获得泛型类型实例化的运行时表示。幸运的是,ITPUB个人空间 j?(IRNt |'h,U `
都向后与 Tiger 兼容。这样的向后兼容编码方案是ITPUB个人空间"z-~H8U*Dw
公式的显著特点(在参考资料一节中)。
(pl0{,Y;x_?9a0 

  可用的超类构造函数                                                            
:Z+Y6GK.l0  在我们希望允许类继承ITPUB个人空间 |jb[)IA
构造函数?请回忆:每个 J
.Xu,z9A6j_0并确保存在匹配的超级构造
f(x^'E!v|a0 类型参数时立即出现的紧迫问题
K L$] QnU cH0ava 类构造函数都必须调用超类
7S$j}An a0函数,类型检查器确保这些超级ITPUB个人空间"J P0Z_fJ
 是要决定我们能调用什么样的超级
0O:\)C SI7hE @Z0的构造函数。通常,通过查找超类
y7E$A,p(HI9D0构造函数调用会成功。ITPUB个人空间NY$Oxt"bm1r
 

  但是在我们对超类所知
ak6F2Wc%|H0用于给定的实例化,我们没
/N"La8t eV)CPh0mixin 实例化都会产生有效ITPUB个人空间)n8` v%h;H+e-e1pw
参数可能用类型参数界限实ITPUB个人空间 P/U7ca,d%NBV9R
 的一切只限于它是类型参数的实ITPUB个人空间``:dgV/[
有任何概念。而且请注意,类型
#r me-uw0J0的超级构造函数调用。其原因是ITPUB个人空间x/AC vp
例化了。ITPUB个人空间(F;U Y;\qTkIw\
 例化时,对于什么样的构造函数可ITPUB个人空间W0~(a;n0]"`
检查器甚至不能检查是否每个
SE6U&H$|\]0:在某些其它上下文中,mixin 的

 

  例如,泛型类 JSplitPane< T> 可以创建 Scrollable< T> 的实例。除非我们知道将类型参数 T 实例化为 JSplitPanes 的一切方法,否则我们不能知道在 Scrollable< T> 中调用的超级构造函数是否有效。但是因为 Java 编码允许单独的类编译,所以在类型检查期间,我们不能知道 JSplitPane 的所有实例。


q5^$U^yF0  解决这一问题的各种方案与我们上月
1~#D#J'I s2{b X#c&wm#w0所提出的解决方案完全一致,因为超级构
d8Y J k-oy4HB0类构造函数。让我们回顾一下这些解决方
!T*yo]'f ~0 第 3 部分中讨论的针对检查 new 表达式的类型参数ITPUB个人空间;ii%K]HK|N
造函数调用和 new 表达式都引用了给定类的同一个
l.E;B4o$V"H4H0案:
m-J(^;ID(}![7r"sy0 

  需要一个不带参数的(zeroary)构造函数,用于所有类型参数的实例化。     
8q8Rz!Y#T*q0  当没有匹配的构造函数时,抛出运行时异常。                                     ITPUB个人空间#] dHwrwy
  包含额外的类型参数注释,告知我们这些实例化必须包含哪些构造函数。             ITPUB个人空间 N.F YgIlr
  就如 new 表达式的情ITPUB个人空间OR&BZq _)ntQ
构造函数没有任何意义。而ITPUB个人空间-zu Q+I9r-D$q0Txgo
静态类型检查主要是严格防
/apKW ?V6o n v0 况,前两个解决方案有严重缺陷
Wk.^4bbd0且,当不存在任何匹配的构造函
&B\6~x"Sc8f ^0止那种异常。
8Q`}8p a0 。通常在类定义中包含不带参数的
`/u g2nO"]1z#c$M0数时就抛出异常也不太理想。毕竟

 

  第三种解决方案可能有点繁琐,但是
F x,@idD'wWM8n0都必须拥有的构造函数集。这些注释确切
0ShWQQ tf0样的构造函数。因此,当类型参数 T 用
"^;b&w9}%~+p| cVw0用哪些超级构造函数。如果 T 不包含注
V U%N C,uu@ _@0 它有许多优点。注释类型参数,其中包括所有实例化ITPUB个人空间)zv$X7r Lh0Q;j
地告知我们针对类型参数,我们可以可靠地调用什么
EeaSN.QTv0作泛型类的超类时,T 的注释确切地告知我们可以调ITPUB个人空间ZDlme1FW
释,那么类型检查器会禁止它用作超类。
L0Z A b:d J0 

  意外的方法覆盖                                                                
iO[,q+qZ0  任何 mixin 公式都会
r q,wb)Lv5R0潜在实例化的方法名冲突。ITPUB个人空间Fp3R2Di"[m)j
返回一个 Size 对象,编码ITPUB个人空间|-Ly"FREj
JComponent 的子类)也包ITPUB个人空间 w hK/X9oP9A dUr c
对象的屏幕面积。ITPUB个人空间TT8a9~yo!G
 产生一个非常严重的问题:特定ITPUB个人空间.}yyG.Z
例如,假设类 Scrollable 包含ITPUB个人空间9io S8D_/nk)k
了其水平和垂直尺寸。现在,我ITPUB个人空间9P&|Z]^
含不带任何参数的方法 getSize

 mixin 的方法名可能与其超类的
d(h'Rx d,H&D0不带任何参数的方法 getSize 并
Kh FdjM?wV(v0们假设类 MyTextPane(ITPUB个人空间+K rn3S1}N{6r\
,但返回一个 int,表示调用它的

 

  产生的类显示如下:                                                            

  清单 1. 意外方法覆盖的示例                                                

  class Scrollable< T extends JComponent> extends T { 

   ...                                                                     
E2I-I bcW\*A0rB&N0   Size getSize() {...}                                    ITPUB个人空间W0r4B:Jt_ m
  }                                                                            

  class MyTextPane extends JComponent {   
sS'Y\'xuvZ0   ...                                                                     ITPUB个人空间+okG3Y M%t-P o
   int getSize() {...}                                      
;\'wK Z1BnO:J0  }                                                                            

  new Scrollable< MyTextPane>() 


}*p.JF+o7`J+KO0  随后 mixin 实例化 Scrollable< MyTextPane> 会包含两个带有同样(空)参数类型的方法 getSize,但返回类型不一致!因为我们不能指望类 Scrollable 的程序员或 MyTextPane 的程序员预见这个有问题的 getSize 覆盖(毕竟,他们甚至不可能在同一个开发团队),因此我们称之为意外覆盖。


?d@#Udi0  当 mixin 被明确表述ITPUB个人空间+?J$O7X_RGX0F'C
用类型参数被实例化,因此
5a&P^4_)A` Kk-~8t0盖出现时抛出运行时异常是
1WK3c'Xcb u3{L0如果我们想编写可靠的程序ITPUB个人空间6O6nr4j3u W {#xM
 为泛型类时,意外覆盖的问题特
`'e&e&S S G z[0类型检查器就不能确定意外方法
\`B`4z\L^:iL0无法接受的,因为客户机程序员
H9|1?sY&A.s0,那么我们必须禁止在运行时出ITPUB个人空间UXWpgGBe~ V
 别讨厌。因为 mixin 的父类可能
,u EoYc4`eh*U0覆盖的所有情况。而且,在意外覆
o E2pP^+J{h0无法预测何时将抛出这样的异常。
+mm5|]w0现无法预料的错误。
yfK2x|]!}4K(`0V0 

  另一个解决方案是只隐藏这些相互冲突的方法中的一个,并解析所有匹配的方法调用以引用未隐藏的方法。这个解决方案的问题是我们希望诸如 Scrollable< MyTextPane> 这样的 mixin 实例化可用于调用 Scrollable 对象的上下文以及调用 MyTextPane 对象的上下文中。隐藏 getSize 方法中的任一个都会在这两个上下文中禁止使用 Scrollable< MyTextPane>。


1{n*j;u-Q0  在 1998 年召开的有关编程语言原理ITPUB个人空间 J!c3|-R"f4Gl0d'`9TZ z
上,Felleisen、Flatt 和 KrishnamurthITPUB个人空间T,BVYUy
该问题的一个好的解决方案:基于使用 mITPUB个人空间5t)H2T T$_4? mH2r
用。在这个解决方案中,mixin 包含有这
?2H%k5z^E2@0法。ITPUB个人空间$f4}RC0]5w*L vL }
 的 ACM SIGPLAN-SIGACT 研讨会(请参阅参考资料)ITPUB个人空间]d+t y'}'Kz
i 提出了在 mixin 不属于泛型类型的上下文中针对ITPUB个人空间 KH+F(D Y R;dyfa
ixin 实例化的上下文来解决对相互冲突的方法的引
!j6w7o"FZW&_0样的观点:确定在名称不一致的情况中要调用哪个方

 

  在 mixin 作为泛型类ITPUB个人空间? o,D~4d
点,这些观点在泛型类型的ITPUB个人空间:s:aI r5W7JP
室中,我们已经在“A Firs
(SA5l(i5O$]1T!s{0出了这样一种解决方案。ITPUB个人空间%D {KjZ:I,q?
 型的情况中,我们可以应用同样ITPUB个人空间?;SLs |'WK
上下文中有效,并且还允许向后ITPUB个人空间.]i"c gzK6I
t-Class Approach to Generici

 的解决方案。我们只要设计一些观
?C3d+rD _S2E)`)[N0兼容 JVM。在 Rice JavaPLT 实验
Mt&W,Si p0ty”(请参阅参考资料)一文中提

 

  有得必有失                                                                    
(_8h0E1Z:`8n\w0  正如示例、问题和可能的解决方案所ITPUB个人空间P"Q"~"`.^}RQv
mixin 的支持会产生一种功能强大的语言ITPUB个人空间*Z&u a'PIXW,f&L7gv
编程语言设计:只能通过使许多现有功能ITPUB个人空间X7F;?N'`-^m0?PfP
没有任何免费的午餐。
v-Z]j6mgZ3R0 
4jp1p.U8c:o)_6w@0 
p:e'd p!vN0发表于:2004.12.24 16:54


TAG:

 

评分:0

我来说两句

显示全部

:loveliness: :handshake :victory: :funk: :time: :kiss: :call: :hug: :lol :'( :Q :L ;P :$ :P :o :@ :D :( :)

Open Toolbar