<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title><![CDATA[Heck's  Blog]]></title> 
<link>https://www.heckjj.com/index.php</link> 
<description><![CDATA[一瞬间的决定，往往可以改变很多，事实上，让自己成功的往往不是知识，是精神！ 如果你总是为自己找借口，那只好让成功推迟。执行力，今天！]]></description> 
<language>zh-cn</language> 
<copyright><![CDATA[Heck's  Blog]]></copyright>
<item>
<link>https://www.heckjj.com/java50-Generics-Types-html/</link>
<title><![CDATA[Java 5.0 泛型详解]]></title> 
<author>Heck &lt;@hecks.tk&gt;</author>
<category><![CDATA[编程杂谈]]></category>
<pubDate>Mon, 20 Sep 2010 19:24:17 +0000</pubDate> 
<guid>https://www.heckjj.com/java50-Generics-Types-html/</guid> 
<description>
<![CDATA[ 
	<span style="font-family: 微软雅黑;"><strong><span style="color: #4169E1;">Java 5.0 泛型简介</span></strong><br/>泛型其实并不是一种新的语言元素，C++中早就就有，但是在C++之后的java却没有吸收这个特性，现在Java也有了泛型的特性，大概也和.Net的竞争有关系吧。<br/><br/>首先看泛型的一个应用。<br/><br/>在过去，我们可能经常要写一些类似这样的代码：</span><br/><textarea name="code" class="java" rows="15" cols="100">
List stringList=new LinkedList();
stringList.add(&quot;firstString&quot;);
stringList.add(&quot;secondString&quot;);
String str=(String)stringList.iterator().next();
</textarea><br/><span style="font-family: 微软雅黑;">实际上第三行对String的类型转换意义并不大，因为通常我们如果在操作一个List，都是知道这个List里面放的是什么类型对象的，但是我们如果不这样写又通不过语法检查。<br/>利用java的泛型机制，我们可以这么写：</span><br/><textarea name="code" class="java" rows="15" cols="100">
List&lt;String&gt; stringList=new LinkedList&lt;String&gt;();
stringList.add(&quot;firstString&quot;);
stringList.add(&quot;secondString&quot;);
String str=stringList.iterator().next();
</textarea><br/><span style="font-family: 微软雅黑;"><br/>这样做的好处是在定义容器的时候就指明了容器中的类型，一方面我们不再需要取一个元素时候做强制类型转换，另外一方面如果在这个容器中放入的对象类型不符合要求，那么会在编译时候产生一个错误，而不是在运行时候才抛出一个异常。<br/>另外这样也提高了程序的可读性。</span><br/><br/><span style="font-family: 微软雅黑;"><strong><span style="color: #4169E1;">泛型类型的定义</span></strong></span><br/>下面是一个简单的使用泛型类的定义：<br/><textarea name="code" class="java" rows="15" cols="100">
public class MyGenericClass&lt;T&gt; &#123;
private T value;
public T getValue() &#123;
return value;
&#125;
public void setValue(T value) &#123;
this.value = value;
&#125; 
&#125;</textarea><br/><span style="font-family: 微软雅黑;">值得注意的一点是，静态变量不能够使用泛型定义，也就是说类似下面的语句是非法的：</span><br/><textarea name="code" class="java" rows="15" cols="100">public class MyGenericClass&lt;T&gt; &#123;
public static T value;//错误的定义
&#125;</textarea><br/><span style="font-family: 微软雅黑;">此外，泛型的定义不会被继承，举个例子来说，如果A是B的子类，而C是一个声明了泛型定义的类型的话，C&lt;A&gt;不是C&lt;B&gt;的子类。为了更好的说明，可以看下面的代码，这段代码是错误的。<br/></span><textarea name="code" class="java" rows="15" cols="100">
List&lt;String&gt; strList =new ArrayList&lt;String&gt;();
List&lt;Object&gt; objList=strList;&nbsp;&nbsp;//错误的赋值</textarea><br/><span style="font-family: 微软雅黑;">不过这样一段代码是正确的：</span><br/><textarea name="code" class="java" rows="15" cols="100">List&lt;Object&gt; strList =new ArrayList&lt;Object&gt;();
strList.add(&quot;a string&quot;);</textarea><br/><br/><span style="font-family: 微软雅黑;"><strong><span style="color: #4169E1;">统配类型</span></strong><br/>假设我们需要这样一个函数，使用它可以把一个集合中所有的元素打印出来，在以前我们可能这样定义：</span><br/><textarea name="code" class="java" rows="15" cols="100">void printCollection(Collection c) &#123;
Iterator i = c.iterator();
&nbsp;&nbsp;for (k = 0; k &lt; c.size(); k++)
&nbsp;&nbsp;&#123; 
&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(i.next());
&nbsp;&nbsp;&#125;
&#125;
</textarea><br/><span style="font-family: 微软雅黑;"><br/>使用新的泛型特性我们可以这样写：</span><br/><textarea name="code" class="java" rows="15" cols="100">
void printCollection(Collection&lt;Object&gt; c) 
&#123; 
&nbsp;&nbsp;for (Object e : c) 
&nbsp;&nbsp;&#123; 
&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(e);
&nbsp;&nbsp;&#125;
&#125;</textarea><br/><br/><span style="font-family: 微软雅黑;">但是这样有一个问题，假如我们现在有个对象类型是Collection&lt;String&gt;，那么我们不能够将它作为参数传给printCollection，因为Collection&lt;String&gt;并不是Collection&lt;Object&gt;的子类。<br/>为了解决这个问题，我们可以使用统配类型?，也就是定义成下面这个样子：</span><br/><textarea name="code" class="java" rows="15" cols="100">
void printCollection(Collection&lt;?&gt; c) 
&#123; 
&nbsp;&nbsp;for (Object e : c) 
&nbsp;&nbsp;&#123; 
&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(e);
&nbsp;&nbsp;&#125;
&#125;</textarea><br/><br/><span style="font-family: 微软雅黑;">可以说Collection&lt;?&gt;是所有Collection的父类。<br/>再来看一段下面的代码</span><br/><textarea name="code" class="java" rows="15" cols="100">
private void clearAllMaps(Collection&lt;Map&gt; c)
&#123;
&nbsp;&nbsp;for(Map m:c)
&nbsp;&nbsp;&#123;
&nbsp;&nbsp; m.clear();
&nbsp;&nbsp;&#125;
&#125;</textarea><br/><br/><span style="font-family: 微软雅黑;">毫无疑问，它也存在上面我们所说的问题，也就是对HashMap之类Map的子类无法进行操作，但是如果我们将参数改成Collection&lt;?&gt;又不大合理，因为我们只希望对父类为Map的子类进行操作，那么我们可以这样改写：<br/></span><textarea name="code" class="java" rows="15" cols="100">
private void clearAllMaps(Collection&lt;? extends Map&gt; c)
&#123;
&nbsp;&nbsp;for(Map m:c)
&nbsp;&nbsp;&#123;
&nbsp;&nbsp; m.clear();
&nbsp;&nbsp;&#125;
&#125;
</textarea><br/><br/><span style="font-family: 微软雅黑;">类似于? extends Map之类的统配符称为限定统配类型。<br/>假设一个对象h类型为Collection&lt;HashMap&gt;，那么我们将h作为参数传给clearAllMaps，如下面一段代码所示：</span><br/><textarea name="code" class="java" rows="15" cols="100">
List&lt;HashMap&lt;String,String&gt;&gt; h=new ArrayList&lt;HashMap&lt;String,String&gt;&gt;();
HashMap&lt;String,String&gt; m=new HashMap&lt;String,String&gt;();
m.put(&quot;key&quot;,&quot;value&quot;);
h.add(m);
clearAllMaps(h);
</textarea><br/><br/><span style="font-family: 微软雅黑;">对于在类似于上面所说，使用了? extend XXX的方法，值得注意的一点是不能够在方法体内用XXX的子类对象作为代替。如下面一段代码是错误的：<br/></span><textarea name="code" class="java" rows="15" cols="100">
public void addRectangle(List&lt;? extends Shape&gt; shapes) 
&#123; 
&nbsp;&nbsp;shapes.add(0, new Rectangle()); // 错误用法!
&#125;
</textarea><br/><br/><span style="font-family: 微软雅黑;">这里我们假设Rectangle是Shape的一个子类。<br/>不允许这样写的原因比较简单，因为调用该方法时候参数类型可能是Shape的另外一个子类。假如说Shape除了Rectangle这个子类以外还有另外一个子类Circle，那么我们可以把一个List&lt;Circle&gt;类型的对象作为参数传给这个方法（注意这样是合法的），而在方法体内却把一个Rectangle对象放到了shapes里面，这显然是不合理的。<br/>除了extends，在泛型参数类型中还可以使用super关键字，参照下面一段程序：</span><br/><textarea name="code" class="java" rows="15" cols="100">
private void addString(Collection &lt;? super String&gt; c)
&#123;
&nbsp;&nbsp;c.add(&quot;a String&quot;);
&#125;
</textarea><br/><br/><span style="font-family: 微软雅黑;"><strong><span style="color: #4169E1;">泛型函数</span></strong><br/>我们在前面提到了统配类型，现在让我们来设想一个函数，它实现这样的功能，将一个数组中的元素添加到一个Collection中，为了保证程序的通用性，我们可能会写出另外一段错误的代码：</span><br/><textarea name="code" class="java" rows="15" cols="100">
private void fromArrayToCollection(Object[] a, Collection&lt;?&gt; c) 
&#123; 
&nbsp;&nbsp;for (Object o : a) 
&nbsp;&nbsp;&#123; 
&nbsp;&nbsp;&nbsp;&nbsp;c.add(o); // 错误的代码
&nbsp;&nbsp;&#125;
&#125;</textarea><br/><br/><span style="font-family: 微软雅黑;">那么这个函数应该怎么写呢？我们可以通过对函数添加泛型参数的方法实现，如下面所示：</span><br/><textarea name="code" class="java" rows="15" cols="100">
private &lt;T&gt; void&nbsp;&nbsp;exfromArrayToCollection(T[] a,&nbsp;&nbsp;Collection&lt;T&gt; c) 
&#123; 
&nbsp;&nbsp;for (T o : a) 
&nbsp;&nbsp;&#123; 
&nbsp;&nbsp;&nbsp;&nbsp;c.add(o); //这样是正确的
&nbsp;&nbsp;&#125;

&#125;</textarea><br/><br/><span style="font-family: 微软雅黑;">那么，在什么时候我们应该使用统配类型，什么时候我们应该使用泛型函数呢？答案是取决于函数参数之间，函数参数和返回值之间的类型依赖性。<br/>如果一个函数的参数类型与函数返回的参数没有必然关联，同时对于该函数其他的参数的类型也没有依赖关系，那么我们就应该使用统配符，否则就应该使用泛型函数。<br/><br/>为了更清楚地说明这一点，我们可以看一下java.util包中Collections类型几个方法的定义：</span><br/><textarea name="code" class="java" rows="15" cols="100">
class Collections &#123; 
static void swap(List&lt;?&gt; list, int i, int j) &#123;...&#125;
static &lt;T&gt; void&nbsp;&nbsp;copy(List&lt;? super T&gt; dest, List&lt;? extends T&gt; src)&nbsp;&nbsp;&#123;...&#125;
&#125;</textarea><br/><br/><span style="font-family: 微软雅黑;">其中swap函数实际上也可以这样定义：</span><br/><textarea name="code" class="java" rows="15" cols="100">
static &lt;T&gt;void swap(List&lt;T&gt; list, int i, int j) &#123;...&#125;
</textarea><br/><span style="font-family: 微软雅黑;"><br/>但是注意到这里泛型类型参数T只在参数中用到了一次，也就是说它和函数其他部分没有依赖性，这可以看作是我们应该使用?的一个标志。<br/>copy方法中，拷贝源src中的元素必须是dest所能够接受的，src中的元素必须是T的一个子类，但是具体它是哪种子类我们又不必关心，所以方法中使用了泛型作为一个类型参数，同时也用了统配类型作为第二类型参数<br/><br/><span style="font-size: 14px;"><span style="color: #4169E1;"><strong>原生类型的autoboxing和auto-unboxing</strong></span></span><br/>我们知道，在Java中，int,long等原生类型不是一个继承自Object的类，所以相应的，有很多操作我们都不能利用原生类型操作，比如想要把一个整数放入到一个集合中，我们必须首先创建一个Integer对象，然后再将这个对象放入到集合中。当我们从集合中取数的时候，取出来的是一个Integer对象，因此不能直接对它使用加减乘除等运算符，而是必须用Integer.intValue()取到相应的值才可以，这样的过程称之为boxing和unboxing。<br/><br/>J2SE5.0支持autoboxing和auto-unboxing，也就是说我们以后不需要再手动地做这些boxing和unboxing操作了，java语言会替我们完成。具体可以参照下面的示例：<br/></span><textarea name="code" class="java" rows="15" cols="100">
List&lt;Integer&gt; intList=new ArrayList&lt;Integer&gt;();
intList.add(2);
intList.add(new Integer(5));
int i=3+intList.get(0);//i=5
int j=3+intList.get(1); //j=8
</textarea><br/><span style="font-family: 微软雅黑;">从这一段程序中我们可以看到，autoboxing和auto-unboxing为我们省掉了很多不必要的工作。</span><br/>Tags - <a href="https://www.heckjj.com/tags/java5.0/" rel="tag">java5.0</a> , <a href="https://www.heckjj.com/tags/%25E6%25B3%259B%25E5%259E%258B/" rel="tag">泛型</a>
]]>
</description>
</item><item>
<link>https://www.heckjj.com/java50-Generics-Types-html/#blogcomment</link>
<title><![CDATA[[评论] Java 5.0 泛型详解]]></title> 
<author> &lt;user@domain.com&gt;</author>
<category><![CDATA[评论]]></category>
<pubDate>Thu, 01 Jan 1970 00:00:00 +0000</pubDate> 
<guid>https://www.heckjj.com/java50-Generics-Types-html/#blogcomment</guid> 
<description>
<![CDATA[ 
	
]]>
</description>
</item>
</channel>
</rss>