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

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

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

轻松掌握 Java 泛型 (第 2 部分)ITPUB个人空间)Fya,L#tUb d]{
来源:** 收集整理

 
l$x,y*oqj.k g0 ITPUB个人空间|CY5X8R!E
 
+cROj.X)\)Sf-{0 ITPUB个人空间{w u f,z,Ab
 ITPUB个人空间\Cm*y Z"UJ:^
     J2SE 1.5 — 代号为“
d`8v$[3{0JSR-14 原型编译器中预先ITPUB个人空间X2wh-R U/@5pP f
型类型的基础知识,以及为
y+k&E9K;B"k_t0明了为 Tiger 制定的泛型ITPUB个人空间Jtg9}+D
型类型的上下文。ITPUB个人空间/Y)g.Q6Aq5] Cm B@
 Tiger” — 计划在 2003 年年
y/uZcA$k:c0展示的那样,现在可下载获得)
K p.c8F:s}:jR0什么它们是对 Java 语言的一个ITPUB个人空间/QCcQ&x
类型的实现怎么会包含数个“缺

 底发布,它将包括泛型类型(如在
|BU7}fL0。在第 1 部分中,我们讨论了泛ITPUB个人空间6P,d\ Oq1_!zr
重要且迫切需要的补充。我们还说
B`'|XLPR2LAf T0陷”,这些缺陷限制了可以使用泛

 

  为了帮助新程序员有效ITPUB个人空间B3E2u~'JQ8k5mS:t
Tiger 和 JSR-14 中是被禁ITPUB个人空间 _r[5kb5U'R6jY
)为了在 JVM 上兼容地实ITPUB个人空间?N8u\+Q
 地使用泛型类型,我将详细说明
4cD!dQe`8mO4Q0止的,并将说明为什么这些限制ITPUB个人空间QW1l-kP`"?
现泛型类型所使用的实现策略的ITPUB个人空间-Q+D%|J4{Cl
 到底泛型类型的哪些用法在
X~ o7s~)U0是 JSR-14(理所当然还有 Tiger
2g5GQ~!Ai;~0\C:B0必然结果。
4T'G[ [u9pGT"A0 

  泛型类型的限制

  让我们先查阅一下 Tiger 和 JSR-14 中泛型类型的使用限制:        

  不应在静态成员中引用封闭类型参数。                                           
8bE8EtK Uyt0  不能用基本类型实例化泛型类型参数。                                           ITPUB个人空间B2I+nP-_t.M:N^
  不能在数据类型转换或 instanceof 操作中使用“外露”类型参数。     ITPUB个人空间7_)S2e,kG&}
  不能在 new 操作中使用“外露”类型参数。                                 
y3x#d"\]KO-s:UX0  不能在类定义的 impleITPUB个人空间N]p2g?7U.~| _W
 ments 或 extends 子句中使用ITPUB个人空间&R\ FVjB1B
 “外露”类型参数。ITPUB个人空间D"O RR0LfI$l
 ITPUB个人空间/{I0ZGHVP9I U@
  为什么会有这些限制呢?这要归因于
/m2lI(PW.Wrf"Vu0的机制。由于 JVM 根本不支持泛型类型ITPUB个人空间2n;E8?'m}K E)g w
泛型类型的支持 — 它们用泛型类型信息ITPUB个人空间z1| S*O*Q v]"t
生成只包含普通类型的类文件。ITPUB个人空间U#dI V,Hs
 Tiger 和 JSR-14 为在 JVM 上实现泛型类型所使用ITPUB个人空间}'s2?;_~ p
,所以这些编译器“耍了个花招”,使得似乎存在对
#S`l|g6y6_?$_M0检查所有的代码,但随即“擦除”所有的泛型类型并

 

  例如,将象 List< T> 这样的泛型类型擦除得只剩下 List。“外露”类型参数 — 单独出现而不是位于某个类型中的类型参数(如类 List< T> 中的类型参数 T)— 被简单地擦除成它们的上界(就 T 而言,其上界就是 Object)。


!?,E-RP u+k/v0  这一技术的功能极其强大;我们可以使几乎所有泛型类型的精度得到增强,但又与 JVM 保持兼容。事实上,我们甚至可以交替地使用非泛型的旧类(比如 List)和其对应的泛型类(List< T>);两者在运行时看起来是一样的。

ITPUB个人空间 kv8U(_Gnpy[
  遗憾的是,正如以上的限制所示,获ITPUB个人空间`]?*n*j B!|
系统中引入了缺陷,这些缺陷限制我们使ITPUB个人空间#^)rW?4kX*mK3[
 得这一功能是有代价的。以这种方式进行擦除在类型ITPUB个人空间Qr9R IiABK
用泛型类型的安全性。ITPUB个人空间p|D J8te$jf
 

  为了帮助说明每种限制ITPUB个人空间`M b|'v Z)mG
限制。与后两个限制有关的ITPUB个人空间 mC:W)@5II
 ,我们查阅会出现这些限制的示ITPUB个人空间bb*nzWtB
问题过于复杂,因而需要更深入
6n;Oe6dw u$B#E0 例。在本文中,我们将讨论前三个
WFKIi4ug0的研究,留待下一篇文章讨论。ITPUB个人空间 c*m8C3^;@\1L4hR1A
 

  静态成员中的封闭类型参数

  编译器完全禁止在静态方法和静态内
Lb'D |Ju0码在 Tiger 中就是非法的:ITPUB个人空间BaD0w*{2?lRC
 部类中引用封闭类型参数。所以,举例来说,以下代

 

  清单 1. 在静态上下文中非法引用封闭类型参数 class C< T> {

   static void m() {                                          
Ru5xU2IT#Wef%L0    T t;                                                                  ITPUB个人空间"J5S*T c?fA(j
   }                                                                          

   static class D {                                            ITPUB个人空间$n+vL9^6R h$?9{
    C< T> t;

   }                                                                          ITPUB个人空间.^W%AT;B5rXF
  }                                                                            

  当编译这一代码时,会生成两个错误:                                            

  在静态方法 m 中非法引用 T 的错误                                       
U&W]4@%?}0  在静态类 D 中非法引用 T 的错误                                         
2C,OI#ey0  当定义静态字段时,情ITPUB个人空间J#@#?Dz
中共享该类中的静态字段。
H,KJttg0明中引用类型参数,编译器ITPUB个人空间R uP'j;Ry3I
时导致奇怪的错误,如在不ITPUB个人空间DNP[g)h-a_(pt0`X
 况变得更加复杂。在 JSR-14 和ITPUB个人空间"{z8v F@d
现在,在 JSR-14 编译器 1.0
OCS]'y#S3w X0不会报错,但它本应该这么做。
A}(pG8f7uy0包含数据类型转换的代码中出现
j_Rnt3c0 Tiger 中,在泛型类的所有实例ITPUB个人空间%L#m,S\+a8?
和 1.2 中,如果您在静态字段声
k-_*RN6b,e0字段被共享这一事实很容易在运行
,p,c Uv`LV$ej x@0ClassCastException。
4~4Q|9v5]5z7u9y0 

  例如,以下程序将在这两个版本的 JSR-14 下通过编译而没有任何警告:      

  清单 2. 在静态字段中对封闭类型参数的有问题的引用 class C< T> {

   static T member;                                            

   C(T t) { member = t; }                                

   T getMember() { return member; }            

   public static void
*z7N6O,UsH*Wnu X0 main(String[] args) {
6u&fy*^ U K#_/w0 ITPUB个人空间*Y7Jn%Y)B,RDz
 ITPUB个人空间 `B?_-Cm:M;@e\/a
    C< String> c = new C< String>("test");

    System.out.print
sZB q'F0 ln(c.getMember().toString())ITPUB个人空间?9f3\\%e$d&R
 ;ITPUB个人空间|({+U G!P t
 
L Cq2[P,d ~'C q0    new C< Integer>(new Integer(1));

    System.out.println(c.getMemb
+q!md$T Rm']0 er().toString());
w-MNe@fiI0 ITPUB个人空间g"z&E l&{wu$U
   }                                                                          
6dSZ/MZ6L$fO AC0  }                                                                            

  请注意,每次分配类 C 的实例时,都要重新设置静态字段 member。而且,它被设置成的对象类型取决于 C 的实例的类型!在所提供的 main 方法中,第一个实例 c 是 C< String> 类型。而第二个是 C< Integer> 类型。每当从 c 访问 member 这一共享静态字段时,总是假定 member 的类型是 String。但是,在分配了类型为 C< Integer> 的第二个实例之后,member 的类型是 Integer。

ITPUB个人空间:t8C;xzT
  运行 C 的 main 方法的结果可能会ITPUB个人空间,p1ZH/M7L|]$d
源代码根本没有包含任何数据类型转换,
]9rp'gHsu8J?0据类型转换插入到代码中,这样做是为了
&I sDC%| |!vA'Pn0实。这些数据类型转换被期望能够成功,ITPUB个人空间$`9J9GE:q
 让您吃惊 — 它将发出一个 ClassCastException!
bQNn#~:d'b0怎么会这样呢?事实证明编译器确实在编译阶段将数ITPUB个人空间x#_/cq&M(c
解决类型擦除会降低某些表达式的类型的精度这一事
(I-cZ'c1} J&q0但在本例中却没有成功。
NG$l0|;Y;P0 

  应该认为 JSR-14 1.0ITPUB个人空间:k-f}%RH L1c8s B
性,或者可以说,它破坏了ITPUB个人空间&R7|i1v1Seg
做的那样,只要防止程序员ITPUB个人空间5Z2^'b\:Rl7I&Yz
 和 1.2 的这一特殊“功能”是ITPUB个人空间 orE!w'JT `
类型系统应该和程序员达成的“ITPUB个人空间;ab ]'eZgd0f
在静态字段中引用泛型类型,情ITPUB个人空间D!D-e6n8i!I
 个错误。它破坏了类型系统的健全ITPUB个人空间` ]NOvY_
基本契约”。象对静态方法和类所
#\wTW~d(~0况就会好很多。ITPUB个人空间2t-X7R$U5u!@` TRj*`
 

  请注意允许这种有潜在
b`#r6g$SRl0码中覆盖类型系统。问题是
ZQ MI+W o0操作,错误地在字段声明中ITPUB个人空间.f |-e)zV5W.T3r
 “爆炸性”的代码存在所带来的ITPUB个人空间A#R0s F%]2Ay P*Y
程序员可能会无意中编写这样的
5I IQL@$x0包括静态修饰符)。
Yx Y&v"bX%@'W0 问题并不是程序员有意在自己的代ITPUB个人空间({G#r9qFe
代码(比如,由于“复制和粘贴”

 

  类型检查器应该能帮助程序员从这些ITPUB个人空间b*{(T)K'[ |
实际上会使程序员更迷惑。当未使用数据
*lGcKe+eO&j\0ClassCastException 时,我们应如何诊
#` m5vH7@6}8M0的实现方案而又恰好假定类型系统合理运
8}-c;F(^4ak$L ^0类型系统不是合理地运行。
} Ro] S!AU)b/?^0 类型的错误中恢复,但对于静态字段而言,类型系统ITPUB个人空间:ZNc ?4O,? ve^
类型转换的代码中显示的唯一错误就是ITPUB个人空间+~0F6l+s4lX(p i
断这样的错误?对于不清楚 Tiger 中泛型类型所用ITPUB个人空间I` e!w u9@*m
行的程序员而言,情况更糟。因为在这样的情况下,

 

  幸运的是,JSR-14 的
Y1dP cMiP/V7l0此,我们有理由期待在 TigITPUB个人空间)}ym.p;vIE
 最新版本(1.3)宣布在静态字ITPUB个人空间 [(v(^B0{!o6j K
er 的静态字段中使用类型参数
"tY"HW ]s!@wv1o0 段中使用类型参数是不合法的。因
-x0Y3PWi9F\ c0也是不合法的。
I(p o;ud0 

  泛型类型参数和基本类型

  和我们刚才讨论的不同,这一限制没有同样的潜在缺陷,但它会使您的代码非常冗长。例如,在 java.util.Hashtable 的泛型版本中,有两种类型参数:用于 Key 类型的和用于 Value 类型的。因此,如果我们想要一个将 String 映射到 String 的 Hashtable,我们可以用表达式 new Hashtable< String, String>() 指定新的实例。但是,如果我们想要一个将 String 映射到 int 的 Hashtable,我们只能创建 Hashtable< String, Integer> 的实例,并将所有的 int 值包装在 Integer 中。

ITPUB个人空间u0^-?Q6P h c8X
  同样,Tiger 在这方面ITPUB个人空间,`#q3mc9XC(u&\4c
界限,而界限不能是基本类ITPUB个人空间 Y2xyfe xrF6N
ITPUB个人空间tB)HF Kr;X _Sz
 当然也是由所用的实现方案得到
S3O3KX];M0型,所以一旦类型被擦除,则对

 的。既然类型参数被擦除为它们的
7]!?6?$Ja6eJ5Xz0基本类型的实例化会完全没有意义

 

  数据类型转换或 instanceof 操作中的“外露”参数

  回想一下,对于“外露”类型参数,我们是指在词汇上单独出现的类型参数,而不是更大类型的语法子组件。例如,C< T> 不是“外露”类型参数,但(在 C 主体中)T 是。

ITPUB个人空间c#Ce.I9L
  如果在代码中对“外露”类型参数进
C"M:p fUC q0出名为“unchecked”的警告。例如,以ITPUB个人空间an3W'KiZ \ PlU
type T:
+M:H c(xU#Q(Uj6X0 行数据类型转换或 instanceof 操作,则编译器将发ITPUB个人空间g@ PWc4[0x
下代码将生成警告:Warning: unchecked cast to

 

  清单 3. 带 unchecked
szM?;_EeD0 操作的泛型代码 import java.ITPUB个人空间 |*Z4DF+\,o[
 util.Hashtable;
[K.I]q&yS-N/Ae0 ITPUB个人空间qb-E/XPlU
  interface Registry {                                      
!GyAnJ0   public void register(Object o);              ITPUB个人空间w!Ot L bv,Fl
  }                                                                            ITPUB个人空间@E rGEV.R\7k
  class C< T> implements Registry {

   int counter = 0;                                            ITPUB个人空间3] Xc,^9\A uD j$H
   Hashtable< Integer, T> values;


]vgv%N g0   public C() {                                                    
G(t zf1i[ B0    values = new Hashtable< Integer, T>();

   }                                                                          

   public void register(Object o) {            
To+yY{qX9hm0    values.put(new Integer(count
!O^k.};|]0OXH0O4d|0 er), (T)o);
W i/R6W%n$D/b%^0 ITPUB个人空间[)JJ:F4T*c
    counter++;                                                      ITPUB个人空间 {6m.Z%cCf
   }                                                                          
-fAW eJ1K{#Yr9Ly0  }                                                                            

  您应该严肃地对待这些警告,因为它们说明您的代码在运行时会表现得非常奇怪。事实上,它们会使得诊断代码变得极为困难。在以前的代码中,我们认为如果对实例 C< JFrame> 调用 register("test"),会发出 ClassCastException。但并非如此;计算将继续,就仿佛数据类型转换成功了一样,然后在进一步进行计算时发出错误,或者更糟:用遭破坏的数据完成计算,但不向外发出任何错误信号。同样,对“外露”类型参数的 instanceof 检查将在编译时产生“unchecked”警告,而且检查将不会如期在运行时进行。


D3i)D6i~n8GHZr;]0  双刃剑

  那么,这里到底发生了
D!b&C-J@7es;s0instanceof 测试中的外露
j^%~3[!w*im.q;X0r0Object)。因此,对类型
JE,W)s2aIq;WYF*o1s0 什么?因为 Tiger 依靠类型擦ITPUB个人空间'c'U8rQ8N$SAR
类型参数被“擦除”为它们的上
)H;D8oc.@"Vd zO*M/z0参数的数据类型转换将变成对参ITPUB个人空间&l)q{yu&L'p5aO
 除,所以数据类型转换和ITPUB个人空间6N'zA dPD1_q5@$s(rz
界(在前面的例子中,那将是类型
W,E$[&S{)U9J0数上界的转换。
FfVL@k)B0 

  同样,instanceof 将ITPUB个人空间%X&LzR@ l
做的,如果是的话,我们完
r}T%prs0数据类型转换和 instanceoITPUB个人空间 JR k!Nb4q,D p
 检查操作数是否是参数界限的 iITPUB个人空间9l] o)Y A1L4n0Q
全可以显式地强制转换为界限。ITPUB个人空间 H5I:Xqbf4ec6e A.x A
f 检查。
,i/OSz%pe+b0 nstanceof。那根本不是我们打算
F!sxz-B:m0因此,通常应避免对类型参数使用

 

  然而,有时为了编译代码,您必须依
v;xb'wh m8[NH0只要记住,在代码的那一部分中,类型检ITPUB个人空间9O J wR}`
 靠对类型参数的数据类型转换。如果是这样的情况,ITPUB个人空间%f h8pE#G0I
查不保险 — 要靠自己。
B$~:ee:Q0 

  尽管泛型类型是制作健
n+n9y s/n0而且极难诊断和修正。下次ITPUB个人空间(I?0aW9p9ie]st
型 Java 类型系统中包括它ITPUB个人空间 C9i/S~g:V{?
 壮代码的强大武器,但我们已经
7h ~e6j&{0,我们将介绍 Tiger 中泛型类ITPUB个人空间.KIr}!h7C
们时必定会出现的一些问题。ITPUB个人空间1O f*\!e'DD$k\d3s
 ITPUB个人空间9]P7lD#}?
 
$V/u,??\/{3z!\,_0发表于:2004.12.24 16:49


TAG:

 

评分:0

我来说两句

显示全部

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

Open Toolbar