`
gogole_09
  • 浏览: 206032 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

读 IBM中国 《Java 理论和实践: 了解泛型》

 
阅读更多

 

了解Java泛型

  参考于IBM Developer 中国 《Java 理论和实践: 了解泛型

 

  Java语言的泛型类似于C++中的模板. 但是这仅仅是基于表面的现象。
 
  Java语言的泛型基本上完全在编译器中实现的,由编译器执行类型检查和类型推断,然后生成普通的非泛型的字节码。 这种实现称为"擦除" 
 (编译器使用泛型类型信息保证类型安全,然后在生成字节码之前将其清除)
  
  泛型不是协变的
    协变:
    Java 语言中的数组是协变的(covariant),也就是说,
    如果 Integer  扩展了 Number (事 实也是如此),那么不仅 Integer  是 Number ,而且 Integer[]  也是 Number[] ,在要求Number[]  的地方完全可以传递或者赋予 Integer[]
    (更 正式地说,如果 Number  是 Integer  的超类型,那么 Number[]  也是 Integer[]  的超类型)
    
    但是,泛型并不是协变的。   如果,List<Number> 是List<Integer> 的超类型,但是,如果需要List<Integer>的时候, 并不容许传递List<Number>,它们并不等价。
    不允许的理由很简单,这样会破坏要提供的类型安全泛型. 如下代码:
        
public static void main(String[] args) {
    List<Integer> list=new ArrayList<Integer>();
    List<Number> list2=list;//编译错误.
    list2.add(new Float(19.0f));
}

    其他协变问题
     数组能够协变而泛型不能协变的另外一个问题是:不能实例化泛型类型的数组: (new List<String>[3]是不合法的 ),除非类型参数是一个未绑定类型的通配符(new List<?>[3]是合法的 ).

    延迟构造
     因为可以擦除功能,所以List<Integer>和List<String>是同一个类,编译器在编译List<V>的时候,只生成一个类。
     所以,运行时,不能区分List<Integer>和List<String>(实际上,运行时都是List,类型被擦除了),用泛型类型参数标识类型的变量的构造就成了问题。运行时缺乏类型信息,这给泛型容器类和希望创建保护性副本的泛型类提出了难题。
    比如泛型类Foo:
    
class Foo<T>{
    public void dosomething(T param){
          T copy=new T(param);//语法错误.
    }
}
      因为编译的时候,还不知道要构造T的具体类型,所以也无法调用T的构造函数。
    那么,我们是否可以使用克隆来构建呢?
class Foo<T extends Cloneable>{
public void dosomething(T param){
    //编译错误,
    T copy=(T)param.clone();
}
}
     为什么会报错呢?   因为clone()在Object类中是受保护的。

    所以,不能复制在编译时根本不知道是什么类的类型引用。

    构造通配符引用
     那么使用通配符类型怎么样呢? 假设要创建类型为Set<?>的参数的保护性副本。 我们来看看:
    
class Foo{
public void doSomething(Set<?> set){
    //编译出错,你不能用通配符类型的参数调用泛型构造函数
    Set<?> copy=new HashSet<?>(set);
}
}

    但是下面的方法可以实现:
    
class Foo{
public void doSomething(Set<?> set){
    Set<?> copy=new HashSet<Object>(set);
}
}

    构造数组
     对于我们常用的ArrayList<V> ,我们需要来探讨一下它的内部实现机制:
    假设它内部管理着一个V数组,那么我们希望能在ArrayList<V>的构造函数中来初始化这个数组:
    
class ArrayList<V>{
V[] content;
private static final int DEFAULT_SIZE=10;
public ArrayList() {
        //编译错误。
        content=new V[DEFAULT_SIZE];
}
}
    但这段代码不能工作,不能实例化用类型参数表示的类型数组。因为编译器不知道V到底是代表什么类型,所以不能实例化V数组。

    Java库中的 Collections提供了一种思路,用于实现,但是非常别扭(设置连Collections的作者都这样说过.) 在Collections类编译时,会产生警告:
class ArrayList<V> {
    private V[] backingArray;
    public ArrayList() {
    backingArray = (V[]) new Object[DEFAULT_SIZE];
    }
}

    因为泛型是通过擦除实现的, backingArray  的类型实际上就是  Object[] ,因为  Object  代替了  V
    这意味着:实际上这个类期望 backingArray  是一个 Object  数组,但是编译器要进行额外的类型检查,以确保它包含 V  类型的对象。所以这种方法很奏效,但是非常别扭,因此不值得效仿
    另外有一种方法是:  
        声明backingArray为Object数组,并在使用它的各个地方,强转成V[]

    其他方法
    
    最好的方法是: 向构造方法中,传入类对象,这样,在运行时,就可以知道T的值了。不采用这种方法的原因是,它无法与之前版本的Collections框架相兼容。
   比如:
 
public class ArrayList<V> implements List<V> {
private V[] backingArray;
private Class<V> elementType;
public ArrayList(Class<V> elementType) {
this.elementType = elementType;
backingArray = (V[]) Array.newInstance(elementType, DEFAULT_LENGTH);
}
}

    但是,这里还是有地方不是很妥当:  
    调用  Array.newInstance()  时会引起未经检查的类型转换。为什么呢?同样是由于向后兼容性。 Array.newInstance()  的签名是:
public static Object newInstance(Class<?> componentType, int length) 
    而不是类型安全的:
public static<T> T[] newInstance(Class<T> componentType, int length) 

 为何  Array  用这种方式进行泛化呢?同样是为了保持向后兼容。要创建基本类型的数组,如  int[] , 可以使用适当的包装器类中的  TYPE  字段调用  Array.newInstance() (对于  int ,可以传递  Integer.TYPE  作为类文字)。用 Class<T>  参数而不是  Class<?>  泛化  Array.newInstance() ,对 于引用类型有更好的类型安全,但是就不能使用  Array.newInstance()  创建基本类型数组的实例了。也许将来会为引用类型提供新的  newInstance()  版本,这样就两者兼顾了。

在这里可以看到一种模式 —— 与泛型有关的很多问题或者折衷并非来自泛型本身,而是保持和已有代码兼容的要求带来的副作用。

    
    擦除的实现
     因为泛型基本上都是在JAVA编译器中而不是运行库中实现的,所以在生成字节码的时候,差不多所有关于泛型类型的类型信息都被“擦除”了, 换句话说,编译器生成的代码与手工编写的不用泛型、检查程序类型安全后进行强制类型转换所得到的代码基本相同。与C++不同,List<Integer>和List<Number>是同一个类(虽然是不同的类型,但是都是List<?>的子类型。)
    擦除意味着,一个类不能同时实现 Comparable<String>和Comparable<Number>,因为事实上,两者都在同一个接口中,指定同一个compareTo()方法。

    擦除也造成了上述问题,即不能创建泛型类型的对象,因为编译器不知道要调用什么构造函数。 如果泛型类需要构造用泛型类型参数来指定类型的对象,那么构造函数应该传入类对象,并将它们保存起来,以便通过反射来创建实例。

PS :
   貌似发布上来格式有问题,
  这里放出我整理的Google文档地址,感觉看不清楚的可以去这里看看。
分享到:
评论
8 楼 kimmking 2010-07-07  
syntax sugar
7 楼 szcjlssx 2010-07-07  
driftcloudy 写道
szcjlssx 写道
我感觉,JAVA中的泛型就是为了解决“类不是对象”这个问题的
因为类不能放到变量中进行传递,所以在动态性方面就捉襟见肘
特别针对容器这样的东西时,由于放入到其中的值类型是运行时决定的,所以对于静态类型的JAVA,需要向上转型,而向上转型则使得对象的使用又出现了问题(又要再向下转型,向下转型容易出错)
而泛型就解决了这个问题,但是我还是希望,将Class也作为对象
像下面的代码
class Demo {
}

public class MainClass {
     public static void main(String args[]) {
          Class d=Demo;//希望这一行代码能够正常运行
     }

}


没明白LS想表达什么意思。我来说点正统的,java泛型的产生一般是认为基于这样两个原因。一是泛型可以提升我们写的代码的复用性,我们希望自己的code可以被更多类型的对象所重用,例如著名的java collection框架。二是泛型带来的好处是将类型强转错误的发现从运行时提前到了编译时。


也就和这意思差不多,就是JAVA想实现一点动态,但JAVA本身的对类的实现就是同一个类不能有不同的名称引用(使用反射API及Class.forName可以达到这个效果,但那样的获得的类仍然不能像原始的类名一样使用,既Class a=Class.forName("org.pl4cj.javademo.Demo");然后new a()是不行的)。另一方面就是,JAVA要实现一点动态但又要保证它的安全,所以引入泛型语法
6 楼 beneo 2010-07-06  
在bytecode层面根本没有范型,这就是说范型对性能没有任何变化。

纯粹让你用的舒服
5 楼 driftcloudy 2010-06-29  
szcjlssx 写道
我感觉,JAVA中的泛型就是为了解决“类不是对象”这个问题的
因为类不能放到变量中进行传递,所以在动态性方面就捉襟见肘
特别针对容器这样的东西时,由于放入到其中的值类型是运行时决定的,所以对于静态类型的JAVA,需要向上转型,而向上转型则使得对象的使用又出现了问题(又要再向下转型,向下转型容易出错)
而泛型就解决了这个问题,但是我还是希望,将Class也作为对象
像下面的代码
class Demo {
}

public class MainClass {
     public static void main(String args[]) {
          Class d=Demo;//希望这一行代码能够正常运行
     }

}


没明白LS想表达什么意思。我来说点正统的,java泛型的产生一般是认为基于这样两个原因。一是泛型可以提升我们写的代码的复用性,我们希望自己的code可以被更多类型的对象所重用,例如著名的java collection框架。二是泛型带来的好处是将类型强转错误的发现从运行时提前到了编译时。
4 楼 szcjlssx 2010-06-29  
我感觉,JAVA中的泛型就是为了解决“类不是对象”这个问题的
因为类不能放到变量中进行传递,所以在动态性方面就捉襟见肘
特别针对容器这样的东西时,由于放入到其中的值类型是运行时决定的,所以对于静态类型的JAVA,需要向上转型,而向上转型则使得对象的使用又出现了问题(又要再向下转型,向下转型容易出错)
而泛型就解决了这个问题,但是我还是希望,将Class也作为对象
像下面的代码
class Demo {
}

public class MainClass {
     public static void main(String args[]) {
          Class d=Demo;//希望这一行代码能够正常运行
     }

}

3 楼 incredible 2010-06-28  
这是为什么啊,刷屏贴啊
2 楼 llfzy 2010-06-28  
IBm......哎。。。
1 楼 qiushily2030 2010-06-26  
<div class="quote_title">gogole_09 写道</div>
<div class="quote_div">
<p> </p>
<div>
<h1 style="font-size: 18pt; text-align: center;">了解Java泛型</h1>
<p>  参考于IBM Developer 中国 《<span style="font-size: 27px; font-weight: bold;"><span style="color: #999999;"><span style="font-weight: normal;"><span style="font-size: small;"><a href="http://www.ibm.com/developerworks/cn/java/j-jtp01255.html" target="_blank">Java 理论和实践: </a></span></span></span><span style="font-weight: normal;"><span style="font-size: small;"><a href="http://www.ibm.com/developerworks/cn/java/j-jtp01255.html" target="_blank">了解泛型</a> </span></span></span>》</p>
<p> </p>
  Java语言的泛型类似于C++中的模板. 但是这仅仅是基于表面的现象。
<div style="margin-top: 0px; margin-bottom: 0px;"> 
<div style="margin-top: 0px; margin-bottom: 0px;">  Java语言的泛型基本上完全在编译器中实现的,由编译器执行类型检查和类型推断,然后生成普通的非泛型的字节码。 这种实现称为"擦除" </div>
<div style="margin-top: 0px; margin-bottom: 0px;"> (编译器使用泛型类型信息保证类型安全,然后在生成字节码之前将其清除)<br><div style="margin-top: 0px; margin-bottom: 0px;">  </div>
</div>
<div style="margin-top: 0px; margin-bottom: 0px;">  <strong><span style="font-size: medium;">泛型不是协变的</span> </strong>
</div>
<div style="margin-top: 0px; margin-bottom: 0px;">    协变:<br>
</div>
<div style="margin-top: 0px; margin-bottom: 0px;">    Java 语言中的数组是协变的(covariant),也就是说,</div>
<div style="margin-top: 0px; margin-bottom: 0px;">    如果 <span style="font-family: 'Courier New';">Integer</span>  扩展了 <span style="font-family: 'Courier New';">Number</span> (事 实也是如此),那么不仅 <span style="font-family: 'Courier New';">Integer</span>  是 <span style="font-family: 'Courier New';">Number</span> ,而且 <span style="font-family: 'Courier New';">Integer[]</span>  也是 <span style="font-family: 'Courier New';">Number[]</span> ,在要求<span style="font-family: 'Courier New';">Number[]</span>  的地方完全可以传递或者赋予 <span style="font-family: 'Courier New';">Integer[]</span> 。</div>
<div style="margin-top: 0px; margin-bottom: 0px;">    (更 正式地说,如果 <span style="font-family: 'Courier New';">Number</span>  是 <span style="font-family: 'Courier New';">Integer</span>  的超类型,那么 <span style="font-family: 'Courier New';">Number[]</span>  也是 <span style="font-family: 'Courier New';">Integer[]</span>  的超类型)</div>
<div style="margin-top: 0px; margin-bottom: 0px;">    </div>
<div style="margin-top: 0px; margin-bottom: 0px;">    但是,<span style="color: #ff0000;">泛型并不是协变的。 </span>  如果,List&lt;Number&gt; 是List&lt;Integer&gt; 的超类型,但是,如果需要List&lt;Integer&gt;的时候, 并不容许传递List&lt;Number&gt;,它们并不等价。<br>
</div>
<div style="margin-top: 0px; margin-bottom: 0px;">    不允许的理由很简单,这样会破坏要提供的类型安全泛型. 如下代码:<br>
</div>
<div style="margin-top: 0px; margin-bottom: 0px;">        <br><table id="egkt" style="font-size: 1em; line-height: inherit; border-collapse: collapse;" border="1" cellspacing="0" cellpadding="3" width="100%"><tbody><tr style="text-align: left;">
<td width="100%">
<div style="margin-top: 0px; margin-bottom: 0px;">public static void main(String[] args) {</div>
<div style="margin-top: 0px; margin-bottom: 0px;">    List&lt;Integer&gt; list=new ArrayList&lt;Integer&gt;();</div>
<div style="margin-top: 0px; margin-bottom: 0px;">    List&lt;Number&gt; list2=list;<span style="color: #ff0000;">//编译错误.</span> </div>
<div style="margin-top: 0px; margin-bottom: 0px;">    list2.add(new Float(19.0f));</div>
<div style="margin-top: 0px; margin-bottom: 0px;">}</div>
</td>
</tr></tbody></table>
<br>
</div>
<div style="margin-top: 0px; margin-bottom: 0px;"><span style="font-size: medium;"><strong>    其他协变问题</strong> </span></div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<strong>    </strong> <span>数组能够协变而泛型不能协变的另外一个问题是:不能实例化泛型类型的数组: (<span style="color: #ff0000;">new List&lt;String&gt;[3]是不合法的</span> ),除非类型参数是一个未绑定类型的通配符(<span style="color: #ff0000;">new List&lt;?&gt;[3]是合法的</span> ).</span> </div>
<span style="font-size: medium;"><br></span>
<div style="margin-top: 0px; margin-bottom: 0px;">   <strong><span style="font-size: medium;"> 延迟构造</span> </strong>
</div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<strong>  <span>  </span> </strong><span>因为可以擦除功能,所以List&lt;Integer&gt;和List&lt;String&gt;是同一个类,编译器在编译List&lt;V&gt;的时候,只生成一个类。</span> </div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span>     所以,运行时,不能区分List&lt;Integer&gt;和List&lt;String&gt;(实际上,运行时都是List,类型被擦除了),用泛型类型参数标识类型的变量的构造就成了问题。运行时缺乏类型信息,这给泛型容器类和希望创建保护性副本的泛型类提出了难题。</span> </div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span>    比如泛型类Foo:</span> </div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">    <br></span>
<table id="bc2q" style="font-size: 1em; line-height: inherit; border-collapse: collapse;" border="1" cellspacing="0" cellpadding="3" width="100%"><tbody><tr style="text-align: left;">
<td width="100%">
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">class Foo&lt;T&gt;{</span> </div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">    public void dosomething(T param){</span> </div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;"><span class=" ">         </span> T copy=new T(param);//语法错误.</span> </div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">    }</span> </div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">}</span> </div>
</td>
</tr></tbody></table>
<div style="margin-top: 0px; margin-bottom: 0px;"><span style="font-size: x-small;">      <span>因为编译的时候,还不知道要构造T的具体类型,所以也无法调用T的构造函数。</span> </span></div>
<div style="margin-top: 0px; margin-bottom: 0px;"><span style="font-size: x-small;"><span>    那么,我们是否可以使用克隆来构建呢?</span> <br></span></div>
<table id="k825" style="font-size: 1em; line-height: inherit; border-collapse: collapse;" border="1" cellspacing="0" cellpadding="3" width="100%"><tbody><tr style="text-align: left;">
<td width="100%">
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">class Foo&lt;T extends Cloneable&gt;{</span> </div>
<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 40px; padding: 10px;">
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">public void dosomething(T param){</span> </div>
<div style="margin-top: 0px; margin-bottom: 0px;"><span style="font-size: x-small;">    //编译错误,<br></span></div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">    T copy=(T)param.clone();</span> </div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">}</span> </div>
</blockquote>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">}</span> </div>
</td>
</tr></tbody></table>
<span style="font-size: x-small;">    <span> 为什么会报错呢? </span> <span> 因为clone()在Object类中是受保护的。</span> <span><br></span></span><span><span style="font-size: x-small;"><br></span>    所以,不能复制在编译时根本不知道是什么类的类型引用。</span> </div>
<span style="font-size: x-small;"><br></span>
<div style="margin-top: 0px; margin-bottom: 0px;">   <strong><span style="font-size: medium;"> 构造通配符引用</span> </strong>
</div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<strong>    </strong> <span>那么使用通配符类型怎么样呢? 假设要创建类型为Set&lt;?&gt;的参数的保护性副本。 我们来看看:</span> </div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span>    </span> <br><table id="scix" style="font-size: 1em; line-height: inherit; border-collapse: collapse;" border="1" cellspacing="0" cellpadding="3" width="100%"><tbody><tr style="text-align: left;">
<td width="100%">
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">class Foo{</span> </div>
<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 40px; padding: 10px;">
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">public void doSomething(Set&lt;?&gt; set){</span> </div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">    //编译出错,你不能用通配符类型的参数调用泛型构造函数</span> </div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">    Set&lt;?&gt; copy=new HashSet&lt;?&gt;(set);</span> </div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">}</span> </div>
</blockquote>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">}</span> </div>
</td>
</tr></tbody></table>
<span style="font-size: x-small;"><br></span>    但是下面的方法可以实现:<br>
</div>
<div style="margin-top: 0px; margin-bottom: 0px;">    <br><table id="nm1r" style="font-size: 1em; line-height: inherit; border-collapse: collapse;" border="1" cellspacing="0" cellpadding="3" width="100%"><tbody><tr style="text-align: left;">
<td width="100%">
<div style="margin-top: 0px; margin-bottom: 0px;">class Foo{</div>
<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 40px; padding: 10px;">
<div style="margin-top: 0px; margin-bottom: 0px;">public void doSomething(Set&lt;?&gt; set){</div>
<div style="margin-top: 0px; margin-bottom: 0px;">    Set&lt;?&gt; copy=new HashSet&lt;Object&gt;(set);</div>
<div style="margin-top: 0px; margin-bottom: 0px;">}</div>
</blockquote>
<div style="margin-top: 0px; margin-bottom: 0px;">}</div>
</td>
</tr></tbody></table>
<br>
</div>
<div style="margin-top: 0px; margin-bottom: 0px;">    <span style="font-size: medium;"><strong>构造数组</strong> </span>
</div>
<div style="margin-top: 0px; margin-bottom: 0px;"><span style="font-size: medium;"><strong>    </strong> <span>对于我们常用的ArrayList&lt;V&gt; ,我们需要来探讨一下它的内部实现机制:</span> <span><br></span></span></div>
<div style="margin-top: 0px; margin-bottom: 0px;"><span style="font-size: x-small;"><span>    假设它内部管理着一个V数组,那么我们希望能在ArrayList&lt;V&gt;的构造函数中来初始化这个数组:</span> <br></span></div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">    <br></span>
<table id="d3m2" style="font-size: 1em; line-height: inherit; border-collapse: collapse;" border="1" cellspacing="0" cellpadding="3" width="100%"><tbody><tr style="text-align: left;">
<td width="100%">
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">class ArrayList&lt;V&gt;{</span> </div>
<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 40px; padding: 10px;">
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">V[] content;</span> </div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">private static final int DEFAULT_SIZE=10;</span> </div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">public ArrayList() {</span> </div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">        //编译错误。</span> </div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">        content=new V[DEFAULT_SIZE];</span> </div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">}</span> </div>
</blockquote>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">}</span> </div>
</td>
</tr></tbody></table>
<span style="font-size: x-small;">    <span>但这段代码不能工作,不能实例化用类型参数表示的类型数组。因为编译器不知道V到底是代表什么类型,所以不能实例化V数组。<br></span></span>
</div>
<span><br></span>
<div style="margin-top: 0px; margin-bottom: 0px;"><span style="font-size: x-small;"><span>    Java库中的 Collections提供了一种思路,用于实现,但是非常别扭(设置连Collections的作者都这样说过.) 在Collections类编译时,会产生警告:</span> <br></span></div>
<table id="uuaw" style="font-size: 1em; line-height: inherit; border-collapse: collapse;" border="1" cellspacing="0" cellpadding="3" width="100%"><tbody><tr style="text-align: left;">
<td width="100%">
<div style="margin-top: 0px; margin-bottom: 0px;">class ArrayList&lt;V&gt; {</div>
<div style="margin-top: 0px; margin-bottom: 0px;">    private V[] backingArray;</div>
<div style="margin-top: 0px; margin-bottom: 0px;">    public ArrayList() {</div>
<div style="margin-top: 0px; margin-bottom: 0px;">    backingArray = (V[]) new Object[DEFAULT_SIZE];</div>
<div style="margin-top: 0px; margin-bottom: 0px;">    }</div>
<div style="margin-top: 0px; margin-bottom: 0px;">}</div>
</td>
</tr></tbody></table>
<span style="font-size: x-small;"><br></span>
<div style="margin-top: 0px; margin-bottom: 0px;"><span style="font-size: x-small;">   <span> 因为泛型是通过擦除实现的,</span> <span>backingArray</span> <span> 的类型实际上就是 </span> <span>Object[]</span> <span>,因为 </span> <span>Object</span> <span> 代替了 </span> <span>V</span> <span>。</span> </span></div>
<div style="margin-top: 0px; margin-bottom: 0px;"><span>    这意味着:实际上这个类期望 <span>backingArray</span>  是一个 <span>Object</span>  数组,但是编译器要进行额外的类型检查,以确保它包含 <span>V</span>  类型的对象。所以这种方法很奏效,但是非常别扭,因此不值得效仿<br></span></div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span>    另外有一种方法是:  </span> </div>
<div style="margin-top: 0px; margin-bottom: 0px;"><span style="font-size: x-small;"><span>        声明backingArray为Object数组,并在使用它的各个地方,强转成V[]</span> <br></span></div>
<span style="font-size: x-small;"><br></span>
<div style="margin-top: 0px; margin-bottom: 0px;"><span style="font-size: x-small;"><span style="font-size: medium;"><strong>    其他方法</strong> </span><br></span></div>
<div style="margin-top: 0px; margin-bottom: 0px;"><span style="font-size: medium;">    <br></span></div>
<span style="font-size: x-small;">  <span>  最好的方法是: 向构造方法中,传入类对象,这样,在运行时,就可以知道T的值了。不采用这种方法的原因是,它无法与之前版本的Collections框架相兼容。</span> </span>
</div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span>   比如:</span> </div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;"> <br></span>
<table id="l2l9" style="font-size: 1em; line-height: inherit; border-collapse: collapse;" border="1" cellspacing="0" cellpadding="3" width="100%"><tbody><tr style="text-align: left;">
<td width="100%">
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">public class ArrayList&lt;V&gt; implements List&lt;V&gt; {</span> </div>
<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 40px; padding: 10px;">
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">private V[] backingArray;</span> </div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">private Class&lt;V&gt; elementType;</span> </div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">public ArrayList(Class&lt;V&gt; elementType) {</span> </div>
</blockquote>
<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 40px; padding: 10px;">
<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 40px; padding: 10px;">
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">this.elementType = elementType;</span> </div>
</blockquote>
<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 40px; padding: 10px;">
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">backingArray = (V[]) Array.newInstance(elementType, DEFAULT_LENGTH);</span> </div>
</blockquote>
</blockquote>
<blockquote style="margin-top: 0px; margin-right: 0px; margin-bottom: 0px; margin-left: 40px; padding: 10px;">
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">}</span> </div>
</blockquote>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span style="font-size: x-small;">}</span> </div>
</td>
</tr></tbody></table>
<span style="font-size: x-small;"><br></span><span style="font-size: x-small;">   <span> 但是,这里还是有地方不是很妥当:  </span> </span>
</div>
<div style="margin-top: 0px; margin-bottom: 0px;"><span style="font-size: x-small;"><span>    调用 </span> <span>Array.newInstance()</span> <span> 时会引起未经检查的类型转换。为什么呢?同样是由于向后兼容性。</span> <span>Array.newInstance()</span> <span> 的签名是:</span> <br></span></div>
<table id="r9l-" style="font-size: 1em; line-height: inherit; border-collapse: collapse;" border="1" cellspacing="0" cellpadding="3" width="100%"><tbody><tr style="text-align: left;">
<td width="100%"><span style="font-size: x-small;"><span style="font-family: 'Courier New';">public static Object newInstance(Class&lt;?&gt; componentType, int length) </span> <br></span></td>
</tr></tbody></table>
<span style="font-size: x-small;">    而不是类型安全的:</span> <br><table id="pb_n" style="font-size: 1em; line-height: inherit; border-collapse: collapse;" border="1" cellspacing="0" cellpadding="3" width="100%"><tbody><tr style="text-align: left;">
<td width="100%"><span style="font-size: x-small;"><span style="font-family: 'Courier New';">public static&lt;T&gt; T[] newInstance(Class&lt;T&gt; componentType, int length) </span> <br></span></td>
</tr></tbody></table>
<span style="font-size: x-small;"><br></span>
<p style="margin: 0px;"><span style="font-size: x-small;"> <span>为何 </span> <span>Array</span> <span> 用这种方式进行泛化呢?同样是为了保持向后兼容。要创建基本类型的数组,如 </span> <span>int[]</span> <span>, 可以使用适当的包装器类中的 </span> <span>TYPE</span> <span> 字段调用 </span> <span>Array.newInstance()</span> <span>(对于 </span> <span>int</span> <span>,可以传递 </span> <span>Integer.TYPE</span>  作为类文字)。用 <span>Class&lt;T&gt;</span> <span> 参数而不是 </span> <span>Class&lt;?&gt;</span> <span> 泛化 </span> <span>Array.newInstance()</span> <span>,对 于引用类型有更好的类型安全,但是就不能使用 </span> <span>Array.newInstance()</span> <span> 创建基本类型数组的实例了。也许将来会为引用类型提供新的 </span> <span>newInstance()</span> <span> 版本,这样就两者兼顾了。</span> </span></p>
<p style="margin: 0px;"><span>在这里可以看到一种模式 —— 与泛型有关的很多问题或者折衷并非来自泛型本身,而是保持和已有代码兼容的要求带来的副作用。</span> </p>
<span>     </span>
<div style="margin-top: 0px; margin-bottom: 0px;">    <strong><span style="font-size: medium;">擦除的实现</span> </strong>
</div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<strong>    </strong> <span>因为泛型基本上都是在JAVA编译器中而不是运行库中实现的,所以在生成字节码的时候,差不多所有关于泛型类型的类型信息都被“擦除”了, 换句话说,编译器生成的代码与手工编写的不用泛型、检查程序类型安全后进行强制类型转换所得到的代码基本相同。与C++不同,List&lt;Integer&gt;和List&lt;Number&gt;是同一个类(虽然是不同的类型,但是都是List&lt;?&gt;的子类型。)</span> </div>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span>    擦除意味着,一个类不能同时实现 Comparable&lt;String&gt;和Comparable&lt;Number&gt;,因为事实上,两者都在同一个接口中,指定同一个compareTo()方法。</span> </div>
<span><br></span>
<div style="margin-top: 0px; margin-bottom: 0px;">
<span>    擦除也造成了上述问题,即不能创建泛型类型的对象,因为编译器不知道要调用什么构造函数。 如果泛型类需要构造用泛型类型参数来指定类型的对象,那么构造函数应该传入类对象,并将它们保存起来,以便通过反射来创建实例。</span> <span><br></span>
</div>
<div style="margin-top: 0px; margin-bottom: 0px;"><br></div>
<div style="margin-top: 0px; margin-bottom: 0px;">PS : </div>
<div style="margin-top: 0px; margin-bottom: 0px;">   貌似发布上来格式有问题,</div>
<div style="margin-top: 0px; margin-bottom: 0px;">  这里放出我整理的Google文档地址,感觉看不清楚的可以去这里看看。</div>
<div style="margin-top: 0px; margin-bottom: 0px;">  <a href="http://docs.google.com/View?id=dgmjz37_7gp8mrjvv">http://docs.google.com/View?id=dgmjz37_7gp8mrjvv</a> </div>
</div>
</div>
<p> </p>

相关推荐

    Java 理论和实践 了解泛型

    6. 级联泛型:如`List&lt;List&lt;String&gt;&gt;`表示列表的元素是字符串列表。 7. 类型推断:Java编译器可以根据上下文自动推断类型参数,例如在lambda表达式和方法引用中。 8. 对于数组,由于历史原因,Java的泛型不支持...

    Java试题-3:反射和泛型的综合应用

    Java试题-3:反射和泛型的综合应用 Java反射 泛型都是比较高级的应用技术

    关于java基础的泛型的练习

    Java泛型是Java SE 5.0引入的一个重要特性,它极大地增强了代码的类型安全性和可读性。泛型在编程中的应用广泛,特别是在集合框架中,使得我们可以在编译时就检查类型,避免了不必要的类型转换,并且提高了代码的...

    Java泛型_Java中的泛型结构_

    Java泛型是Java编程语言中一个强大的特性,它允许在定义类、接口和方法时使用类型参数,从而实现参数化类型。泛型的主要目标是提高代码的类型安全性和重用性,减少类型转换的麻烦,并在编译时捕获可能的类型错误。...

    java泛型技术之发展

    Java泛型是Java编程语言中的一个关键特性,它在2004年随着Java SE 5.0的发布而引入,极大地增强了代码的类型安全性和重用性。...在实践中,不断探索泛型的各种用法和边界条件,将有助于提升Java编程的专业水平。

    Java基础篇:泛型.pdf

    泛型是Java编程语言中用于减少类型转换错误和增强代码安全性的机制,它允许在定义类、接口和方法时使用类型参数。通过这种方式,可以在编译时期捕获那些只有在运行时期才会暴露的类型错误,提高了代码的健壮性。 ...

    java泛型学习ppt

    * 不使用泛型:ArrayList al1=new ArrayList(); al1.add(new Integer(10)); Integer i1=(Integer)al1.get(0); // 这里必须做强制类型转换。 * 使用泛型:ArrayList&lt;Integer&gt; al2=new ArrayList(); al2.add(new ...

    Java集合框架及泛型

    在实际开发中,理解和熟练运用Java集合框架和泛型能够大大提高代码的可维护性和安全性,减少类型转换的麻烦,并使得代码更易于理解和复用。通过以上讲解,相信你已经对这两个主题有了更深入的理解。通过练习和应用,...

    Java核心知识1:泛型机制详解.pdf

    Java核心知识1:泛型机制详解 泛型机制是Java语言中的一种重要特性,自JDK 1.5开始引入,提供了编译时类型安全检测机制,允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,即给类型指定一个参数,然后...

    Java编程基础教程:枚举与泛型的深入讲解

    内容概要:本文深入介绍了Java中的两个重要特性——枚举和泛型。首先详细讲述了枚举的基本概念、枚举类型的特点及其实现方式,如枚举的成员方法、构造器以及如何将其用于高级编程场合。其次对泛型的概念进行了解释,...

    Java总结篇系列:Java泛型详解

    泛型是Java语言中的一项重要特性,它允许我们在定义类、接口以及方法时使用类型参数,以提高代码的类型安全性和可重用性。泛型的引入主要是为了解决传统Java集合框架中类型转换的问题,避免在运行时出现`...

    Java 实现泛型List

    Java 实现泛型List的源码,基本实现了List接口的全部所有方法。欢迎大家发表自己的观点和建议。

    java泛型类和函数

    ### Java泛型类和函数详解 #### 泛型概述 在Java中,泛型是一种允许开发者在类、接口和方法中使用类型参数的功能。通过使用泛型,可以在编写代码时指定一个或多个类型参数,从而使得编写的代码更加灵活且重用性更高...

    SUN公司Java泛型编程文档

    SUN公司的Java泛型编程文档,包括英文原版和网络翻译版,为开发者提供了深入理解和熟练运用泛型的重要资源。 首先,泛型的基本概念是允许在定义类、接口和方法时使用类型参数,这样就可以在编译时检查类型安全,...

    [Java泛型和集合].(Java.Generics.and.Collections).文字版

    Java泛型和集合是Java编程语言中的核心特性,它们极大地提高了代码的类型安全性和可读性,同时也简化了集合操作。本资料 "[Java泛型和集合].(Java.Generics.and.Collections).Maurice.Naftalin&amp;Philip.Wadler....

    java泛型的内部原理及更深应用

    Java泛型是Java编程语言中的一个强大特性,它允许在定义类、接口和方法时使用类型参数,从而实现参数化类型。这使得代码更加安全、可读性更强,并且能够减少类型转换的必要。在“java泛型的内部原理及更深应用”这个...

Global site tag (gtag.js) - Google Analytics