本站首页    管理页面    写新日志    退出


«December 2025»
123456
78910111213
14151617181920
21222324252627
28293031


公告
 本博客在此声明所有文章均为转摘,只做资料收集使用。

我的分类(专题)

日志更新

最新评论

留言板

链接

Blog信息
blog名称:
日志总数:1304
评论数量:2242
留言数量:5
访问次数:7678837
建立时间:2006年5月29日




[J2SE]generic-泛型/类属(三)
软件技术

lhwork 发表于 2006/8/21 10:02:15

管中窥虎 在学习 java 1.5 的过程中,我使用了 sun 公布的 tutorial ,这份文档写的比较详尽易明,但是对于想快速了解 tiger 而且具有较好 java 基础的人来说,大篇幅的英文文档是比较耗时间和非必需的,所以我将会归纳这份文档的主要内容,在保证理解的底线上,尽力减少阅读者需要的时间。   在以下地址可以进入各新增语言特色介绍以及下载相关文档(若有)。 http://java.sun.com/j2se/1.5.0/docs/relnotes/features.html   这一篇是接着上两篇继续的。 第一道虎纹: generic -泛型 / 类属(三) 一些零碎   关于类:     500)this.width=500'> List  <  String  >  l1  =   new  ArrayList  <  String  >  (); 500)this.width=500'>500)this.width=500'>List  <  Integer  >  l2  =   new  ArrayList  <  Integer  >  (); 500)this.width=500'>500)this.width=500'>System.out.println(l1.getClass()  ==  l2.getClass()); 500)this.width=500'>   这样的代码打印出什么?脑子有没有点混乱?事实是 true ,虽然类型参数不一样,但它们在运行时终归是同一个类。一个 class 可以有不同的 type 。由于静态的变量和方法是被这个类的所有实例共享的,所以在静态的方法,初始化块,静态变量的声明或初始化中引用类型变量(前面的 T 这一类的东西)是非法的。   关于转换和 instanceOf :   正因为类的类型是被所有的实例共享的,所以去问一个实例是否为特定的类型是无意义的。 500)this.width=500'> Collection cs  =   new  ArrayList  <  String  >  (); 500)this.width=500'>500)this.width=500'>500)this.width=500'> if  (cs  instanceof  Collection  <  String  >  )  500)this.width=500'> { 500)this.width=500'> }   //  illegal  500)this.width=500'> 500)this.width=500'> 这样的代码是非法的。   500)this.width=500'> Collection  <  String  >  cstr  =  (Collection  <  String  >  ) cs;  //  unchecked warning    同样的,上面这行代码将会有 unchecked warning , 因为它试图做的类型检查,是根本不会在运行时被执行的。同样的,类型变量也是无效的:   500)this.width=500'> 500)this.width=500'> <  T  >  T badCast(T t, Object o)  500)this.width=500'> {  return  (T) o;  //  unchecked warning  500)this.width=500'> 500)this.width=500'>}  500)this.width=500'>   总而言之,类型变量在运行时是不存在的,意味着它们不会对运行表现增加任何时间上或者空间上的累赘,这样挺好,但同时也意味着,别指望用它们来做类型转换。     关于数组   一个数组对象的元素类型是不能为类型变量或者带类型参数的类型,除非是一个非受限通配符类型,你可以声明一个元素类型为类型变量或者带类型参数的类型的数组类型,但是不能声明这样的数组对象。好吧,你舌头打结了吧?说实话,在翻译这段话前,我不仅舌头打结,连神经都快打结了,即使如此,我还是不能肯定我翻译的是否正确。原文如下: The component type of an array object may not be a type variable or a parameterized type, unless it is an (unbounded) wildcard type.You can declare array types whose element type is a type variable or a parameterized type, but not array objects. 通过阅读下面的代码,也许帮助你理解这团乱麻,之所以有上面这样的复杂规定,是为了避免这样的情形:   500)this.width=500'> List  <  String  >  [] lsa  =   new  List  <  String  >  [ 10 ];  //  实际上不被允许,现在只是假设可以这样写  500)this.width=500'> 500)this.width=500'>Object o  =  lsa; 500)this.width=500'>500)this.width=500'>Object[] oa  =  (Object[]) o; 500)this.width=500'>500)this.width=500'>List  <  Integer  >  li  =   new  ArrayList  <  Integer  >  (); 500)this.width=500'>500)this.width=500'>li.add( new  Integer( 3 )); 500)this.width=500'>500)this.width=500'>oa[ 1 ]  =  li;  //  有错,但通过了运行时的检查  500)this.width=500'> 500)this.width=500'>String s  =  lsa[ 1 ].get( 0 );  //  run-time error - ClassCastException 500)this.width=500'>   如果运行了数组元素为带类型变量的类型,就会出现这样的情况,明明通过了编译,没有任何 unchecked warning ,但在运行时却出错。所以泛型必须设计为这个样子,以保证:如果你的整个应用程序在 jdk1.5 下通过了编译而且没有 unchecked warning ,那它就是类型安全的。 然而,你还可以用通配符数组,接下来是以上代码的两个改版。   第一个让数组类和数组对象都用了通配符,在最后一句,如果要赋值给 String ,就必须用显示的类型转换,虽然出了错,那么可以说这不是机制的错误了,而是程序员的错。   500)this.width=500'> List  <   ?   >  [] lsa  =   new  List  <   ?   >  [ 10 ];  //  ok, array of unbounded wildcard type  500)this.width=500'> 500)this.width=500'>Object o  =  lsa; 500)this.width=500'>500)this.width=500'>Object[] oa  =  (Object[]) o; 500)this.width=500'>500)this.width=500'>List  <  Integer  >  li  =   new  ArrayList  <  Integer  >  (); 500)this.width=500'>500)this.width=500'>li.add( new  Integer( 3 )); 500)this.width=500'>500)this.width=500'>oa[ 1 ]  =  li;  //  correct  500)this.width=500'> 500)this.width=500'>String s  =  (String) lsa[ 1 ].get( 0 );  //  run time error, but cast is explicit 500)this.width=500'>       在第二个版本里,我们定义了带类型参数的类型的数组类,但数组对象用了通配符,这样是合法的,但不安全,产生了 unchecked warning ,而且最终也确实出错了。但至少,我们得到了警告。   500)this.width=500'> List  <  String  >  [] lsa  =   new  List  <   ?   >  [ 10 ];  //  unchecked warning - this is unsafe!  500)this.width=500'> 500)this.width=500'>Object o  =  lsa; 500)this.width=500'>500)this.width=500'>Object[] oa  =  (Object[]) o; 500)this.width=500'>500)this.width=500'>List  <  Integer  >  li  =   new  ArrayList  <  Integer  >  (); 500)this.width=500'>500)this.width=500'>li.add( new  Integer( 3 )); 500)this.width=500'>500)this.width=500'>oa[ 1 ]  =  li;  //  correct  500)this.width=500'> 500)this.width=500'>String s  =  lsa[ 1 ].get( 0 );  //  run time error, but we were warned 500)this.width=500'>       同样的,试图创造一个元素类型是类型变量的数组对象是通过不了编译的:   这些错误的原因归结起来也是我们之前讨论过的了,就是因为运行时,这些类型变量是不存在的,无法决定数组的实际类型。 要突破这些限制,可以使用 类名称字面常量( class literal )作为运行时类型标记,请看下文。     以类名称字面常量作为运行时类型标记 Jdk1.5 里, java.lang.Class 是泛型的,这就有趣了, Class 类有个类型参数 T ,那 T 代表什么? T 代表了 Class 类的对象所代表的类型,又来绕口令了不是? 举例吧: String.class 的类型就是 Class<String> , Serializable.class 的类型就是 Class < Serializable > 。现在 Class 类的 newInstance() 方法返回一个 T ,你现在在创造对象的时候获得的类型更加精确了。( 1.4 里是固定地返回 Object ) 假设你要写一个工具方法,从数据库里执行一个 SQL 查询,将符合查询的结果以对象集合返回,有一钟方法就是显示的使用一个工厂对象,如下: 500)this.width=500'> 500)this.width=500'> interface  Factory  <  T  >   500)this.width=500'> { T make(); }  500)this.width=500'>500)this.width=500'>500)this.width=500'> public   <  T  >  Collection  <  T  >  select(Factory  <  T  >  factory, String statement)  500)this.width=500'> { 500)this.width=500'>500)this.width=500'>       Collection  <  T  >  result  =   new  ArrayList  <  T  >  (); 500)this.width=500'>500)this.width=500'>500)this.width=500'> /**/ /*  run sql query using jdbc  */  500)this.width=500'>500)this.width=500'>500)this.width=500'>      for  ( /**/ /*  iterate over jdbc results  */  )  500)this.width=500'> { 500)this.width=500'>500)this.width=500'>      T item  =  factory.make(); 500)this.width=500'>500)this.width=500'>500)this.width=500'> /**/ /*  use reflection and set all of item’s fields from sql results  */  500)this.width=500'>500)this.width=500'>      result.add(item); 500)this.width=500'>500)this.width=500'>      }  500)this.width=500'>500)this.width=500'>       return  result; 500)this.width=500'>500)this.width=500'>}  500)this.width=500'>500)this.width=500'> 你可以这样调用它:   select( new  Factory  <  EmpInfo  >  () {  public  EmpInfo make() {     return   new  EmpInfo(); } } , ”selection string”);    或者声明一个实现 Factory 接口的类 EmpInfoFactory , class  EmpInfoFactory  implements  Factory  <  EmpInfo  >  500)this.width=500'> {  public  EmpInfo make() {    return   new  EmpInfo(); } }  然后这样调用它: 500)this.width=500'> select(getMyEmpInfoFactory(), ”selection string”); 500)this.width=500'>500)this.width=500'>   这两个办法的缺点就是,你要么在调用处写上罗嗦的工厂类,要么为每个类都写一个工厂类,在每个调用的地方传入一个工厂对象,这看起来也不怎么自然。 用 类名称字面常量来作为一个工厂对象就很自然,它可以用于反射机制,如果不用泛型,可以这样写: C...ollection emps = sqlUtility.select(EmpInfo.class, ”select * from emps”); public   static  Collection select(Class c, String sqlStatement) {    Collection result  =   new  ArrayList();     /*  run sql query using jdbc  */      for  (  /*  iterate over jdbc results  */  ) {        Object item  =  c.newInstance();        /*  use reflection and set all of item’s fields from sql results  */        result.add(item);    }  return  result; }    然而这样我们得不到我们想要的包含确切的类型的对象集,而现在 Class 是泛型的了,我们可以用它来达到目的: Collection  <  EmpInfo  >  emps  =  500)this.width=500'> sqlUtility.select(EmpInfo. class , ”select  *  from emps”);  public   static   <  T  >  Collection  <  T  >  select(Class  <  T  >  c, String sqlStatement) { Collection  <  T  >  result  =   new  ArrayList  <  T  > ();     /*  run sql query using jdbc  */      for  (  /*  iterate over jdbc results  */  ) { T item  =  c.newInstance();        /*  use reflection and set all of item’s fields from sql results  */        result.add(item);    }  return  result; }  精确的类型,类型安全。齐活儿了。   这一篇到此为止,泛型的部分还剩下比较复杂的两个部分,经过考虑后决定延后完成,先完成 tiger 的其他几个简易的新特色会比较有趣些。


阅读全文(1779) | 回复(0) | 编辑 | 精华
 



发表评论:
昵称:
密码:
主页:
标题:
验证码:  (不区分大小写,请仔细填写,输错需重写评论内容!)



站点首页 | 联系我们 | 博客注册 | 博客登陆

Sponsored By W3CHINA
W3CHINA Blog 0.8 Processed in 0.814 second(s), page refreshed 144819564 times.
《全国人大常委会关于维护互联网安全的决定》  《计算机信息网络国际联网安全保护管理办法》
苏ICP备05006046号