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

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

上一篇 / 下一篇  2008-01-10 17:59:03 / 个人分类:做着

轻松掌握 Java 泛型 (第 1 部分)ITPUB个人空间 ZpPK*V:B
来源:** 收集整理
}6VsS%BaKR,c0 ITPUB个人空间Cm5M!}"h oO
 ITPUB个人空间1qN.L~ `j
 

轻松掌握 Java 泛型 (第 1 部分)
#^3[bD&H i'{Sy0来源:** 收集整理ITPUB个人空间`r%oI"fBr Y
 ITPUB个人空间'u S/K:z}1c
 
(F4~M tz0 
%O(z@%nztl0J2SE 1.5 - 代号为 TITPUB个人空间!\F7i] G$[ Ql
集有关即将推出的新技术的ITPUB个人空间 Yx Z\(C*k[5I
新的和经过重组的特性,本
's4G:m {'?j2J0支持它们而进行的更改和调ITPUB个人空间 eWCODKc9u$C*K
 iger - 计划在 2003 年年底发
+c&Wez M*iV+Wa0预告信息,因此我将撰写一系列
#V8Y#n1w4f9Z h X0文是第一篇。我特别想谈谈泛型ITPUB个人空间d"K$p? |I"}
整。
.yTO/` H6h0 布。我一直都热衷于尽可能多地收ITPUB个人空间6M?gd1Ar)xJU
的文章,讨论可从 V1.5 中获得的
B&H.h0w'k0类型并重点讲述在 Tiger 中为了

 

  在许多方面,Tiger 肯
fTIj6?$O0所取得的最大进步。Tiger
X5@ B `9NH7g;[6Bw5n0编译器中所预先展示的那样ITPUB个人空间-zS#S2y'?-N;[Xs*s
 定是迄今为止在 Java 编程方面ITPUB个人空间D7CB_+uZe P
中计划进行的最显著的变化是添ITPUB个人空间gA,tS](IP
(您可以立即免费下载该编译器
zk(X"xI0 (包括对源语言语法的重大扩展)ITPUB个人空间 Y7s"\P%v4^W#h9b
加泛型类型,正如在 JSR-14 原型
1oUp ZbC2u$k0;请参阅参考资料)。ITPUB个人空间I N'TR)Ka%M
 

  让我们从介绍泛型类型是什么以及添加了什么特性来支持它们开始吧。                

  数据类型转换和错误

  为理解泛型类型为何如ITPUB个人空间1o:eBc4o(Wi&W
之一 - 需要不断地将表达ITPUB个人空间4c5zTw5X
(请参阅参考资料中的“Th
ca]J6DoiC$_ \Tv0可能会碰到的麻烦的某些方ITPUB个人空间"^&G{.G3\4q+P(B
 此有用,我们要将注意力转向 J
2\w:S@3d0式向下类型转换(downcast)为ITPUB个人空间;NFB g*qnp
e Double Descent bug patternITPUB个人空间V0G1Iq6J
面)。ITPUB个人空间6cXka d3a
 ava 语言中最容易引发错误的因素
$q;ujf p[0比其静态类型更为具体的数据类型ITPUB个人空间m:BN8a|y Z
”,以了解进行数据类型转换时,

 

  程序中的每个向下类型转换对于 Cla
-GJP*R:Y2?}u Jm0免它们。但是在 Java 语言中它们通常是
O:W!Q6H6a1EM8g0 ssCastException 而言都是潜在的危险,应当尽量避ITPUB个人空间S:],D)p0Y Nd.D J
无法避免的,即便在设计优良的程序中也是如此。
?*b0p4p]&sa:yN.un;T7q0 

  在 Java 语言中进行向下类型转换最
q US g$Dx;c0制了方法调用所返回的参数可能的运行时
Zz~2` l:{3Z H]0检索元素。那么在给定的程序中,被用作ITPUB个人空间f7dE G!\2C(cP
任意对象。通常,所有的键都是某一特定
DD~H-K){3im'AIR0Object 更具体的公共类型。ITPUB个人空间kT9a)Q1Z j$R1U.R _
 常见的原因在于,经常以专用的方式来使用类,这限ITPUB个人空间 G*@/T*e2Gt8~!o
类型。例如,假定往 Hashtable 中添加元素并从中
X(oDWSZ0键的元素类型和存储在散列表中的值类型,将不能是ITPUB个人空间 fe;p0k+Y~(k
类型的实例。同样地,存储的值将共同具有比

 

  但是在目前现有的 JavITPUB个人空间5i,s'{4mY&R7uM
更具体的类型。在散列表上ITPUB个人空间]4Co}0X0Kz+Q6R
象。例如,put 和 get 操ITPUB个人空间q4B"n,R5}U-@x
 a 语言版本中,不可能将散列表
/T` L.B(_x.aE.h0执行插入和检索操作的类型特征ITPUB个人空间#@N&G;j'_!v)T^pR
作的说明如下所示:
]5G:e0Q3a0 的特定键和元素声明为比 ObjectITPUB个人空间:xE!m_J#N2RV-E:f
符告诉我们只能插入和删除任意对

 

  清单 1. 插入/检索类型说明表明只能是任意对象                              

  class Hashtable {                                            ITPUB个人空间r O&CB(@L)s
   Object put(Object key, Object
$q~%KE0Jj"m l0 value) {...}
4z0{j!u s!R)hv0 ITPUB个人空间@i&NAl6Z9yn |Q
   Object get(Object key) {...}                    
jM_3T&ed{.[ Q0   ...                                                                      
rD ?:SnD7v:n5V'R)f:_J8P0  }                                                                            

  因此,当我们从类 HasITPUB个人空间r+|Y1toW1^)q}'p
中只放了 String,而类型ITPUB个人空间/\"E7C8ax)T3s8m"Wy
何特定于 String 的操作之ITPUB个人空间R#vBf[1ml
同一代码块中,也是如此!ITPUB个人空间j`]^go)R
 htable 的实例检索元素时,比
1] r6mX u6Ev B6Q$h6n0系统也只知道所检索的值是 ObjITPUB个人空间 ]f|iz.z~GK8ez}
前,必须将它强制转换为 Strin

 如,即使我们知道在 Hashtable
"b9b D2v!J,c f2e|0ect 类型。在对检索到的值进行任ITPUB个人空间"i @*C;K.~u*r q z _
g,即使是将检索到的元素添加到

 

  清单 2. 将检索到的值强制转换成 String                              

  import java.util.Hashtable;                        
~ v:b3a S0  class Test {                                                      
1d)Rhdyq0   public static voidITPUB个人空间G(`e'RGQ
 main(String[] args) {
Fvdg9yN5{Q?2^0 
/a:XV${%Z0Y\(C0 
$v;]6{;e&b0    Hashtable h = new Hashtable();              ITPUB个人空间$L+D_v%qcy
    h.put(new Integer(0), "value");            
BT a:tR!C PC e)zk0    String s = (StriITPUB个人空间6j7Eb'F&QOD
 ng)h.get(new Integer(0));
Y)HrS4u0 ITPUB个人空间+gn,phx'Q8]Z1c
 ITPUB个人空间#|6f|c0s"v
    System.out.println(s);                              
M'lLL m Ul\0   }                                                                          
%k7{n-Q/Y4e)v)c`+evO0  }                                                                            

  请注意 main 方法主体部分的第三行
2a%fMSz#A9?4\0当薄弱,因此代码会因象上面那样的数据
v-H4G]6B;{o'T0Java 代码变得更加拖沓冗长,而且它们
4H EPI TQ0换都是一个选择忽略静态类型检查的伪指ITPUB个人空间(AA IA']\)^hO
呢?ITPUB个人空间s N"d9V V
 中需要进行的数据类型转换。因为 Java 类型系统相ITPUB个人空间&h;J4Si,C/@X
类型转换而漏洞百出。这些数据类型转换不仅使ITPUB个人空间&[.Cv4AI
还降低了静态类型检查的价值(因为每个数据类型转
|e$h_*{(z,\0令)。我们该如何扩展该类型系统,从而不必回避它

 

  用泛型类型来解决问题!

  要消除如上所述的数据类型转换,有
S*H(Kz+k0系统。可以将泛型类型看作是类型“函数
%{-[N)x9N j1? E(}0以根据上下文用各种类型参数进行实例化ITPUB个人空间U0iF+_&D M$wc2o3y
 一种普遍的方法,就是用泛型类型来增大 Java 类型ITPUB个人空间?0^~L9V}9_
”;它们通过类型变量进行参数化,这些类型变量可ITPUB个人空间+hJ+F?0v&_

;r7MMD[+bt0 

  例如,与简单地定义类 Hashtable 不同,我们可以定义泛型类 Hashtable< Key, Value>,其中 Key 和 Value 是类型参数。除了类名后跟着尖括号括起来的一系列类型参数声明之外,在 Tiger 中定义这样的泛型类的语法和用于定义普通类的语法很相似。例如,可以按照如下所示的那样定义自己的泛型 Hashtable 类:

ITPUB个人空间!\5h M4\2B7}
  清单 3. 定义泛型 Hashtable 类                                  

  class Hashtable< Key, Value> { ... }

ITPUB个人空间['Zn/Z:`#G+rP
  然后可以引用这些类型参数,就像我们在类定义主体内引用普通类型那样,如下所示:  

  清单 4. 像引用普通类型那样引用类型参数 class Hashtable< Key, Value> {

   ...                                                                      
)FE#Z[/EV,a-~ x0   Value put(Key k, Value v) {...}              
3Z lpwlT0   Value get(Key k) {...}                                
&o d;L(w$gM{ nm1R!y o1?0  }                                                                            

  类型参数的作用域就是ITPUB个人空间JI6sWb+lizk2FN
我们将讨论为何 Tiger 实
`Hi*p Q/iK!^&t0!)。ITPUB个人空间u1KUiH!J$c+S
 相应类定义的主体部分(除了静
!t8pTn;r:i0现中有这样的“怪习”,即必须

 态成员之外)(在下一篇文章中,ITPUB个人空间p[f"w3xU2K"z
对静态成员进行此项限制。请留意

 

  创建一个新的 HashtabITPUB个人空间1b%g+Y-dZr T!`,H
递类型参数的方式取决于我ITPUB个人空间Cz;ikY#?
是创建 Hashtable 实例,ITPUB个人空间'f)~{ y2wB'zKM
成这件事:ITPUB个人空间xLF,i"R LhP*Y
 le 实例时,必须传递类型参数
*f$i]A)\k0们打算如何使用 Hashtable。在ITPUB个人空间+p8pj'd p6?
它只将 Integer 映射为 String

 以指定 Key 和 Value 的类型。传
&Q}"_&N,L;a'}F&tc8`0上面的示例中,我们真正想要做的ITPUB个人空间/]+[U4NlS D'Q
。可以用新的 Hashtable 类来完

 

  清单 5. 创建将 Integer 映射为 String 的实例              

  import java.util.Hashtable;                        ITPUB个人空间N~H^&O+CS \
  class Test {                                                      
_w[O?S$H1_K0   public static void main(String
w2q8E7` @$R/i0 [] args) {ITPUB个人空间*@5ER2YgX
 
xYY2`:Gc5h%L:pe0    Hashtable< Integer, String> h = new Hashtable< Integer, String>();

    h.put(new Integer(0), "value");            ITPUB个人空间'g$eVo*G{
    ...                                                                    ITPUB个人空间O G'^$lN6H+Xn.u
   }                                                                          ITPUB个人空间MJ kL?,{
  }                                                                            

  现在不再需要数据类型ITPUB个人空间3k~9Dkv:K'`
类的类型参数用尖括号括起
L r R aIP\H*Gw0 转换了。请注意用来实例化泛型
a+r l.i"IGIRuyG0来那样,泛型类型应用程序的参ITPUB个人空间\}0e[,D;V&L:]p]
 类 Hashtable 的语法。就像泛型ITPUB个人空间%BCNE?
数也是用尖括号括起来的。
i5Lis(kAl*v:Rn0 

  清单 6. 除去不必要的数据类型转换                                          

  ...                                                                        
Y7~li^0  String s = h.get("key");                              ITPUB个人空间A2m JV'u v3I W
  System.out.println(s);                                  

  当然,程序员若只是为
E'|?5h&y:T7A#^0Hashtable 和 List)的话
)MKZ#Q4o[0Java 集合类的泛型版本,
h0v-eGuu|b0码和新的泛型代码一起无缝ITPUB个人空间!|Cv0@9J5yV h
 了能使用泛型类型而必须重新定
A/tZM9R$o_JX0,则可能会是一项浩大的工程。ITPUB个人空间8`LYoi"]U-UP
因此我们不必自己动手来重新定ITPUB个人空间C/}5CITj
工作(下个月,我们会说明如何ITPUB个人空间4d3cp6h7n'e$]P:B4JDY
 义所有的标准实用程序类(比如ITPUB个人空间%w*Y(y#]L
幸好,Tiger 为用户提供了所有
X5f'G2Ga6a U5U+H%D5C0义它们了。此外,这些类能与旧代
*NGV&^qL&n{0做到这一点)。
:MB1sk4him {|0 

  Tiger 的基本类型限制

  Tiger 中类型变量的限制之一就是,
"@AG_m ^ j]0用。因此,在上面这个示例中,无法完成ITPUB个人空间"~P|&i}HJr
 它们必须用引用类型进行实例化 - 基本类型不起作
1d8D%InD h0创建从 int 映射到 String 的 Hashtable。
"Z#w,P#I!t{0 

  这很遗憾,因为这意味
sE Ao.As0为对象。另一方面,当前的ITPUB个人空间Ie`v `N
为所有的键都必须是 Objec
hY W!b6I$k.G s0 着只要您想把基本类型用作泛型ITPUB个人空间}[$Z7As?+}D
这种情况是最糟的;您不能将 iITPUB个人空间 X(s;d)v,S
t 类型。
Z%Q7C`As(jy,d!D0 类型的参数,您就必须把它们组装ITPUB个人空间,}p-RW E`$u
nt 作为键传递给 Hashtable,因

 

  我们真正想看到的是,基本类型可以ITPUB个人空间5mqfGU,qbz
似于用 C# 所进行的操作(或者比后者更ITPUB个人空间,C:Zv3|6us2f P
包装(但是人们可以一直期待 Java 1.6
2AU n)Zd0 自动进行包装(boxing)和解包装(unboxing),类
? w|0@ esCh0好)。遗憾的是,Tiger 不打算包括基本类型的自动
l3L)`7u6U:GLIV0中出现该功能!)。ITPUB个人空间 W r%I @-H3i0g8g0n @0o
 

  受限泛型

  有时我们想限制可能出ITPUB个人空间3?:Y/M}V1d H\
类型参数可以用我们想用的
9r#I*MX Y0能的类型参数集限定为给定ITPUB个人空间0B)m'~wo _ E5[P
 现的泛型类的类型实例化。在上
)ubt,L |+TOyP0任何类型参数进行实例化,但是
+R$Fp_S9g0类型范围内的子类型。ITPUB个人空间Wk*mZJNwc5K9e.g
 面这个示例中,类 Hashtable 的ITPUB个人空间4VL-S$z.~3p:e+\
对于其它某些类,我们或许想将可

 

  例如,我们可能想定义ITPUB个人空间+v1J'W"_P![E
包含的 Pane 的运行时类型ITPUB个人空间5W0Q*L@)c#Y
 泛型 ScrollPane 类,它引用普
WIz!m[9jk%z,`x0通常会是类 Pane 的子类型,但ITPUB个人空间5j)N.t1Q*g
 通的带有滚动条功能的 Pane。被
2C5XrdK9Zkl4M0是静态类型就只是 Pane。
0Eu*s Xi0y7G]#r P0 

  有时我们想用 getter 检索被包含的
V]q2@2u Y&o+H0。我们可能想将类型参数 MyPane 添加到ITPUB个人空间#gk5rI(X%gJ
类进行实例化。然后可以用这种形式的子
G Y l1J;Z!m(c0设定 MyPane 的范围:ITPUB个人空间y"Khya X xW
 Pane,但是希望 getter 的返回类型尽可能具体些ITPUB个人空间&\:l/C!g1h7Uv
ScrollPane 中,该类型参数可以用 Pane 的任何子ITPUB个人空间RiH Hn,]#{;{!t9E
句:extends Bound 来说明 MyPane 的声明,从而来

 

  清单 7. 用 extends 子句来说明 MyPane 声明                

  class ScrollPane< MyPane extends Pane> { ... }


(| H;i"II|0?0  当然,我们可以完全不ITPUB个人空间aMLNCW8u r
数。
(vo7gS2k8v*ev0 使用显式的范围,只要能确保没

 有用不适当的类型来实例化类型参

 

  为什么要自找麻烦在类型参数上设定
ro/J.p+?;j\p0静态类型检查功能。有了静态类型检查,ITPUB个人空间Y:\9f z?(n

E"K:D+Q^ St k0 范围呢?这里有两个原因。首先,范围使我们增加了
_d O(d!C\0就能保证泛型类型的每次实例化都符合所设定的范围

 

  其次,因为我们知道类型参数的每次ITPUB个人空间"Hw C"l ^l8u8Zy
用类型参数实例出现在这个范围之内的任
9d Z!A:f6? t!f~0情况下范围是 Object,这意味着我们不ITPUB个人空间$iA9T6m)\%mM
 实例化都是这个范围之内的子类,所以可以放心地调
TKw!| iwL1z0何方法。如果没有对参数设定显式的范围,那么缺省
Lf-J${o6J0能调用范围实例在 Object 中未曾出现的任何方法。
4V!g!@+}/y\0 

  多态方法

  除了用类型参数对类进ITPUB个人空间*N;Zi[A"si;w6Ju
泛型 Java 编程用语中,用
i!{c)x`Xz0 行参数化之外,用类型参数对方
l#B$H-c.yyNhX?0类型进行参数化的方法被称为多ITPUB个人空间dt9\;^T8q] Ey%J/m
 法进行参数化往往也同样很有用。
'f3}N,u2{"w(Fd V#T0态方法(Polymorphic method)。ITPUB个人空间.H'q8BU-e
 

  多态方法之所以有用,ITPUB个人空间-h F&euz!M~vQfD
类型相关性原本就是泛型的ITPUB个人空间a5tZ.u"e I@
方法调用都不相同。ITPUB个人空间:f$c n$m0HSw ~m2jH
 是因为有时候,在一些我们想执ITPUB个人空间}z/Kr9P(QF2F5K
,但是这个泛型性质不依赖于任

 行的操作中,参数与返回值之间的ITPUB个人空间x;k0nJ hy
何类级的类型信息,而且对于各个

 

  例如,假定想将 factory 方法添加到 List 类中。这个静态方法只带一个参数,也将是 List 唯一的元素(直到添加了其它元素)。因为我们希望 List 成为其所包含的元素类型的泛型,所以希望静态 factory 方法带有类型变量 T 这一参数并返回 List< T> 的实例。

ITPUB个人空间 ?7@4ATS
  但是我们确实希望该类型变量 T 能ITPUB个人空间I z*I CGh*Z2U
调用而发生改变(而且,正如我在下一篇ITPUB个人空间C2kX(p"Ui |}
态成员不在类级类型参数的范畴之内)。
F]y:t S0Hi0从而在单独的方法级别上声明类型参数。
#a*m{9UC e0make 添加前缀:
e$xaS;^^4z0 在方法级别上进行声明,因为它会随每次单独的方法
9o.^w%G&d we7V:sa0文章中将讨论的那样,Tiger 设计的“怪习”规定静
1CP2];_#Et0Tiger 让我们通过将类型参数作为方法声明的前缀,ITPUB个人空间"Y Uv(wtI lU
例如,可以按照如下所示的那样为 factory 方法

 

  清单 8. 将类型参数作为前缀添加到方法声明                                  

  class Utilities {                                            
Cd;Ss,Q/_R/h P0    < T extends Object> public static List< T> make(T first) {

     return new List< T>(first);

    }                                                                        ITPUB个人空间jP$[S-m0{cP o9k1k
  }                                                                            

  除了多态方法中所增加的灵活性之外,Tiger 中还增加了一个优点。Tiger 使用类型推断机制,根据参数类型来自动推断出多态方法的类型。这可以大大减少方法调用的繁琐和复杂性。例如,如果想调用 make 方法来构造包含 new Integer(0) 的 List< Integer> 新实例,那么只需编写:

ITPUB个人空间*{W9\i9B@%re/p
  清单 9. 强制 make 构造新实例                                        

  Utilities.make(Integer(0))                          

  然后会自动地从方法参数中推断出类型参数的实例化。                              

  结束语

  正如我们所见到的那样,在 Java 语
pNYx1X[&r0系统的能力。学习如何使用泛型类型相当
8y/Q-SPYN0章中,我们将讨论如何充分使用将出现在
!m seK2I;d[0我们还将研究对泛型 Java 类型工具的扩
g8L!d#_}cPg"Ld9C0Java 平台之中。
-BP Q6[(N9U3`0 
6W(BZ_3O6\0 发表于:2004.12.24 14:39ITPUB个人空间(Op`S*?#N


TAG:

 

评分:0

我来说两句

显示全部

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

Open Toolbar