SlideShare a Scribd company logo
1 of 7
几年前当 Java5 还未正式发布的时候,看到过一些人写的介绍 Tiger 中的新特性,当时对
我第一感觉冲击最大的就是泛型(generics)和注释(annotation),因为它们直接影响了我
们编码的语法习惯。

在后来的使用过程中,对于泛型一直没有特别深入的使用过,没有遇到那样的需求和场景。
只需要了解 Java 中的泛型是编译期的,运行期被“擦拭”掉了;然后还有几种通配符的表
示就足够了。

直到一天我在查看 Java5 中 Enum 的源代码时,发现它是这么定义的:
public abstract class Enum<E extends Enum<E>> implements Comparable<E>,
Serializable {
这个类似递归结构的 Enum<E extends Enum<E>> 究竟表达什么意思?

随后我又看到了在Collections工具类中的max 方法:
    public static <T extends Object & Comparable<? super T>> T
max(Collection<? extends T> coll) {
怎么TMD还会有这么复杂的泛型表达式!?
(幸好的是这种情况在我们实际开发过程中不多见,甚至不应该见到,大概只有像JDK这种
为了保留对老版本的兼容才会设计出这么复杂的泛型表达式出来。)

上面的问题,你可以通过:http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf
来获取答案,中文版的在:
http://blog.csdn.net/explorers/archive/2005/08/15/454837.aspx
这篇泛型指南的文章,非常详细。

或许你看了上面的文章,依然心存疑虑。我下面的内容则是对这篇文档的一些补充(会和这
篇文档有点重合)。

在回到我之前抛出的问题上:

public static void foo(List<? extends Number> l){
       l.add(new Integer(2)); // 编译通过么? Why ?
   }

    public static void bar(List<? super Number> l){
       l.add(new Integer(2)); // 编译通过么? Why ?
       l.add(new Float(2));   // ok?
   }


这里主要说说 <? extends T> 和 <? super T> 这两种通配符对于方法参数的使用原则。
即 PECS 原则 (producser-extends, consumer-super) 或者也叫 Get and
Put 原则

当没有使用通配符的情况下,我们定义一个方法:
public static <E> void test(List<E> l){
     E e = l.get(0);
     l.set(0, e);
  }
我们从List中 get和set都没有问题,因为这个E 它的类型是某种明确的类型。

而当使用通配符时来描述参数时,就有些不同了。
我们先定义一下两种通配符:
<? extends E> 是 Upper Bound(上限) 的通配符
<? super E> 是 Lower Bound(下限) 的通配符

1) 当使用 Upper Bound 通配符时:
    public static void test(List<?> list){
       Object e = list.get(0); // get OK
       list.set(0, e);           // set 编译报错
    }
上面代码中通配符<?> 是 <? extends Object> 的简写。(关于<?>是否和<? extends
Object>完全等价,我遇到个小插曲,在结束的时候来描述)

在eclipse里错误提示为: The method set(int, capture#2-of ?) in the type
List<capture#2-of ?> is not applicable for the arguments (int, Object)

注: <capture#2-of ?> 是一个占位符,表示编译器对通配符的捕获,更多见:
http://www.ibm.com/developerworks/cn/java/j-jtp04298.html

set报错的原因是因为此时方法中的类型是不可具体化的(reified),你可以传递一个
String,Number,Book,等任何继承自Object的类作为List的参数类型给test方法,而
list要求集合中的类型必须是一致的,set的时候没有办法保证set进去的数据类型是否和
list中原本的类型一致,比如你传给test方法的是 List<Book>, 那么在方法中set进去一
个Object显然类型就不一致了。
这也是通配符带来灵活性的同时所要付出的代价。

结论:使用了 <? extends E> 这样的通配符,test方法的参数list变成了只能get不能
set(除了null) 或者不严谨的说它变成了只读参数了, 有些类似一个生产者,提供数据。

2) 当使用 Lower Bound 的通配符时:
   public static void test(List<? super Number> list){
      Number n = list.get(0);           // 编译错误
      Object o = list.get(0);           // OK

       list.set(0, new Object());      // 编译错误
       list.set(0, new Long(0));       // OK
       list.set(0, new Integer(0));    // OK
   }
这时get只能get出最宽泛的父类型,即Object。
这时set的时候,必须是Number或Number的子类。
原因和上面的get类似。

结论: 使用了<? super E> 这种通配符,test方法的参数list的get受到了很大的制约,
只能最宽泛的方式来获取list中的数据,相当于get只提供了数据最小级别的访问权限(想
想,你可能原本是放进去了一个Book,却只能当作Object来访问)。
它更多适合于set的使用场景,像是一个消费者,主要用来消费数据。

上面便是对通配符的使用原则的说明,简单的说 PECS原则是指导我们在泛型方法中使用通
配符的直接原则。参数作为生产者使用<? extends E>,作为消费者时使用<? super E> 。

那么说完了 PECS原则,我们再回过头来分析那两个复杂的泛型表达式是怎么含义

1) class Enum<E extends Enum<E>>
它确实是一个 “递归类型限制”(recursive type bound)
要说明白这个问题,我们要先明白2点:
a) Enum可以理解为一组同一类型的有限的数据集合;
b) Java对Enum中的数据类型要求必须也是枚举类型(即必须是继承Enum类的)。

对于 a) 我们先定义一个 Enum<T> 表明定义了T这种类型作为它内部的数据类型,这么看
它就像个普通的集合。
再来根据b) 定义类型E,要求E必须是继承自 Enum<T>,便成了 <E extends Enum<T>>
实际上 E和T是一回事,它们是同一类型, 所以它就是 <E extends Enum<E>>

暂停,可能我上面的表达不太合理可能会误人子弟,递归类型限制是有些抽象,它应该有
严谨的数学描述,我想不清楚怎么表达,我先用另一个简单些例子来说明吧

public static <T extends Comparable<T>> T max(List<T> list)
这个方法用来获取list 中的最大值,它定义了一个 <T extends Comparable<T>> 的
类型表达式

这个递归类型限制的表达式容易理解一些,
 <T extends Comparable<T>> 表示的是:针对可以与自身进行比较的每个类型
T。
或者说,每个实现了Comparable<T>接口的类型T,比如 String 实现了
Comparable<String> , Long实现了Comparable<Long>等等

而Enum因为使用enum关键字的原因,让我们忽略了它底层的实现其实也是
class EnumSample extends Enum<EnumSample> 这一事实,
比如 我们定义了 BoundKind 这样的一个枚举:
public enum BoundKind{ }
编译器会转换为:
public final class BoundKind extends java.lang.Enum<BoundKind>
看到了,这和 String implements Comparable<String> 类似
这样我们套回到<E extends Enum<E>> 就是 <BoundKind extends Enum<BoundKind>>
这下好理解了, <E extends Enum<E>> 直接按字面理解:每个继承自Enum<E>的类型
E,比如 BoundKind 继承了 Enum<BoundKind>

通过与<T extends Comparable<T>>的对比,我们可以理解 <E extends Enum<E>>了。

那现在我们再回到Collections工具类中的max 方法:
    public static <T extends Object & Comparable<? super T>> T
max(Collection<? extends T> coll) {

我们先简化一下这个表达式,看看
<T extends Comparable<? super T>> 怎么理解?
既然 <T extends Comparable<T>> 我们都理解了,把Comparable<T> 改为
Comparable<? super T> 也没什么费解的

在《Java1.5 Generics Tutorial》一文中的解释是:
T 精确的(exactly)和自己能比较是不需要的。所需要的 是 T能 够和它的父类中的一个
进行比较。

而《Effictive Java》第二版中对此是用 PECS原则来解释的:

    下面是修改过的使用通配符类型的声明:
    public static <T extends Comparable<? super T>> T max(List<? extends
T> list)
     为了从初始声明中得到修改后的版本,要应用PECS转换两次,最直接的是运用到参数
List。它产生T实例,因此将类型从List<T>改为List<? extends T>。(ok好理解)
。。。
 。。      。
更灵活的是运用到类型参数T。最初T 被指定用来扩展Comparable<T>,但是T的
comparable消费T实例(并产生表示顺序关系的整值)。因此,参数化类型
Comparable<T>被有限制通配符类型Comparable<? super T>取代。               comparable始终
是消费者,因此使用时始终应该是Comparable<? super T> 优先于 Comparable<T>。

蓝色粗体的那句翻译的不好。还是看一下代码来理解吧:

    public static <T extends Comparable<? super T>> T max(Collection<?
extends T> coll) {
   1    Iterator<? extends T> i = coll.iterator();
   2    T candidate = i.next();
   3    while (i.hasNext()) {
   4        T next = i.next();
   5        if (next.compareTo(candidate) > 0) // here comparaTo
   6            candidate = next;
7     }
  8     return candidate;
  9 }

第5行,Bloch认为 next.compareTo(cand) 是一句消费操作,在消费一个candidate对象时,
根据PECS原则,candidate的类型应该使用 <? super T> 来提高它的灵活性。

我觉得Bloch将第5行当作消费操作挺别扭的,我个人偏向《Java1.5 Generics Tutorial》中
的解释。
但归根到底,都是降低限制,提高比较时的灵活性。

最后,我们再来完整的理解:<T extends Object & Comparable<? super T>>
就只是比 <T extends Comparable<? super T>> 多了一个限制(bounds)。

Object & Comparable<? super T> 是一个多限定(multiple bounds)的用法,
语法为: T1 & T2 … & Tn
一个有多个界限的类型的参数是所有界限中列出来的类型的子类。                      当多个界限被使用的时候,
界限中的第一个类型被用作这个类型参数的erasure。

最终这个方法的返回值,按照第一个限定,擦拭为Object类型了。这是因为在以前版本中此
方法就是返回的Object类型,需要兼容。

因为多限定(multiple bounds)的存在,泛型方法中又对应的增加了一个很不优雅的调用
方式。下面用一段代码来说明:

public class GenericsTest {
    static class Book {};
    static class StoryBook extends Book implements
Comparable<StoryBook> {
        @Override
        public int compareTo(StoryBook o) {
              return 0; //FIXME
        }};
    static class TechBook extends Book implements
Comparable<TechBook> {
        @Override
        public int compareTo(TechBook o) {
              return 0; //FIXME
        }};


    public static <E> Set<E> merge(Set<? extends E> s1, Set<? extends
E> s2) {
        HashSet<E> newSet = new HashSet<E>(s1);
        newSet.addAll(s2);
return newSet;
      }


      public static void main(String[] args) {
           HashSet<StoryBook> s1 = new HashSet<StoryBook>();
           HashSet<TechBook> s2 = new HashSet<TechBook>();
           Set<Book> sb = merge(s1, s2); // 错误
           // 需通过显式的类型参数(explicit type parameter)来告诉它要使用哪种
类型
           Set<Book> bs = GenericsTest.<Book>merge(s1,s2); //OK
           // 或者
           Set<Comparable<?>> s = GenericsTest.<Comparable<?
>>merge(s1,s2);
       }
  }


上面直接调用merge(s1,s2) 那行代码错误的提示信息:
Type mismatch: cannot convert from
Set<GenericsTest.Book&Comparable<?>> to Set<GenericsTest.Book>
这归于泛型的类型推导(type inference),当无法推导出明确的类型时,就需要显式的描
述,如上面代码中红色粗体字。



后注:
有关于 <?> 与 <? extends Object>是否是一回事
今天中午发现同事桌上有本        《Java编程思想》  第四版,随手翻了一下,发现泛型一章的介绍
中,有句描述:“UnboundedWildcards.java 展示了编译器处理List<?>和List<? Extends
Object>时是不同的。”
这让我奇怪,查看了一下它的代码,主要因为是对于Raw类型的造型为泛型时的警告信息不
同。
将一个Raw的ArrayList造型给 List<?> 没有问题,而给List<? Extends Object>却会有警
告。

在网上查了一下,发现对于<?>与<? extends Object>是否等同,是有些不同意见
的。
http://mail.openjdk.java.net/pipermail/compiler-dev/2008-April/000316.html
http://bugs.sun.com/view_bug.do?bug_id=6559175
这个报告里,有两段代码反映了两者的不同:
(1)
    public static void main(String[] args) {
        Object customer = null;
        foo((List<? extends String>) customer ); //[1]
        foo((List<? extends Object>) customer ); //[2] 编译有警告
        foo((List<?>) customer ); //[3] 编译没有警告
}
   public static void foo(List<?> list) {
   }

(2)
        Object o2 = new List<?>[3];  // 编译居然OK,估计直接当作Raw处理了
        Object o3 = new List<? extends Object>[3]; // 报错
上面两段代码,表明了当与Raw类型造型时,<?>在编译器的处理方式的确与<? Extends
Object>有所不同,根据场景它可能被编译器忽略掉泛型信息而直接当作Raw类型,而<?
Extends Object>则不会。

但这种差异,有些吹毛求疵,除了跟Raw类型转换方面存在差异,在语义上两者可以认为是
完全等同的,见:http://bugs.sun.com/view_bug.do?bug_id=6480391

The introduction of the capture conversion simplified a lot of things. One of
the things it did is make "?" equivalent to "? extends Object". Unfortunately,
JLS3 doesn't say they are equivalent.

SUN 的开发人员回复说:
? should be considered equivalent to ? extends Object. I will note this at the
end of the text about bounds for wildcards in 4.5.1.
……
Hence, Foo<?> is semantically equivalent to Foo<? extends Object>

但查了一下发现目前 JLS3中还依然没有增加他说要加的那句注释,见:
http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.5.1
我们暂从语义上认为两者相等。

More Related Content

What's hot

线程编程方面
线程编程方面线程编程方面
线程编程方面yiditushe
 
线程与并发
线程与并发线程与并发
线程与并发Tony Deng
 
Java script closures
Java script closuresJava script closures
Java script closuresskywalker1114
 
Erlang jiacheng
Erlang jiachengErlang jiacheng
Erlang jiachengAir-Smile
 
Java面试知识
Java面试知识Java面试知识
Java面试知识yiditushe
 
Java程序员面试之葵花宝典
Java程序员面试之葵花宝典Java程序员面试之葵花宝典
Java程序员面试之葵花宝典yiditushe
 
Ejb工作原理学习笔记
Ejb工作原理学习笔记Ejb工作原理学习笔记
Ejb工作原理学习笔记yiditushe
 
Js对象及函数式编程乱步
Js对象及函数式编程乱步Js对象及函数式编程乱步
Js对象及函数式编程乱步Pierre Woo
 
Java多线程:驾驭Synchronize的方法
Java多线程:驾驭Synchronize的方法Java多线程:驾驭Synchronize的方法
Java多线程:驾驭Synchronize的方法yiditushe
 
Lua 语言介绍
Lua 语言介绍Lua 语言介绍
Lua 语言介绍gowell
 
所谓闭包
所谓闭包所谓闭包
所谓闭包youzitang
 
Java多线程编程详解
Java多线程编程详解Java多线程编程详解
Java多线程编程详解yiditushe
 
Java易犯错误
Java易犯错误Java易犯错误
Java易犯错误yiditushe
 
Js的国(转载)
Js的国(转载)Js的国(转载)
Js的国(转载)Leo Hui
 
深入淺出 Web 容器 - Tomcat 原始碼分析
深入淺出 Web 容器  - Tomcat 原始碼分析深入淺出 Web 容器  - Tomcat 原始碼分析
深入淺出 Web 容器 - Tomcat 原始碼分析Justin Lin
 
所谓闭包
所谓闭包所谓闭包
所谓闭包ilovey4
 

What's hot (20)

线程编程方面
线程编程方面线程编程方面
线程编程方面
 
线程与并发
线程与并发线程与并发
线程与并发
 
Java script closures
Java script closuresJava script closures
Java script closures
 
并发控制
并发控制并发控制
并发控制
 
Erlang jiacheng
Erlang jiachengErlang jiacheng
Erlang jiacheng
 
Java面试知识
Java面试知识Java面试知识
Java面试知识
 
Enum开锁
Enum开锁Enum开锁
Enum开锁
 
Java程序员面试之葵花宝典
Java程序员面试之葵花宝典Java程序员面试之葵花宝典
Java程序员面试之葵花宝典
 
Ejb工作原理学习笔记
Ejb工作原理学习笔记Ejb工作原理学习笔记
Ejb工作原理学习笔记
 
Js对象及函数式编程乱步
Js对象及函数式编程乱步Js对象及函数式编程乱步
Js对象及函数式编程乱步
 
Java多线程:驾驭Synchronize的方法
Java多线程:驾驭Synchronize的方法Java多线程:驾驭Synchronize的方法
Java多线程:驾驭Synchronize的方法
 
Lua 语言介绍
Lua 语言介绍Lua 语言介绍
Lua 语言介绍
 
所谓闭包
所谓闭包所谓闭包
所谓闭包
 
Java多线程编程详解
Java多线程编程详解Java多线程编程详解
Java多线程编程详解
 
Javascript 闭包
Javascript 闭包Javascript 闭包
Javascript 闭包
 
Java Thread
Java ThreadJava Thread
Java Thread
 
Java易犯错误
Java易犯错误Java易犯错误
Java易犯错误
 
Js的国(转载)
Js的国(转载)Js的国(转载)
Js的国(转载)
 
深入淺出 Web 容器 - Tomcat 原始碼分析
深入淺出 Web 容器  - Tomcat 原始碼分析深入淺出 Web 容器  - Tomcat 原始碼分析
深入淺出 Web 容器 - Tomcat 原始碼分析
 
所谓闭包
所谓闭包所谓闭包
所谓闭包
 

Viewers also liked

Προσανατολισμός στο φυσικό και στο δομημένο περιβάλλον - ΚΠΕ Μακρινίτσας
Προσανατολισμός στο φυσικό και στο δομημένο περιβάλλον - ΚΠΕ ΜακρινίτσαςΠροσανατολισμός στο φυσικό και στο δομημένο περιβάλλον - ΚΠΕ Μακρινίτσας
Προσανατολισμός στο φυσικό και στο δομημένο περιβάλλον - ΚΠΕ ΜακρινίτσαςΗλιάδης Ηλίας
 
Comercio electrónico en méxico
Comercio electrónico en méxicoComercio electrónico en méxico
Comercio electrónico en méxicoOmar Sánchez
 
Enfermedades de transmisión sexual
Enfermedades de transmisión sexualEnfermedades de transmisión sexual
Enfermedades de transmisión sexualkabnath1
 
PlAn De MeDiOs
PlAn De MeDiOsPlAn De MeDiOs
PlAn De MeDiOsrobertrin
 
Incorporación de la Gestión de Tecnología como un área del conocimiento en la...
Incorporación de la Gestión de Tecnología como un área del conocimiento en la...Incorporación de la Gestión de Tecnología como un área del conocimiento en la...
Incorporación de la Gestión de Tecnología como un área del conocimiento en la...Academia de Ingeniería de México
 
4a imagen reputacion_villafane
4a imagen reputacion_villafane4a imagen reputacion_villafane
4a imagen reputacion_villafaneAnayde15
 
Preguntas y respuestas de economia capitulos 1 3 anexo ensayo
Preguntas y respuestas de economia capitulos 1 3 anexo ensayoPreguntas y respuestas de economia capitulos 1 3 anexo ensayo
Preguntas y respuestas de economia capitulos 1 3 anexo ensayoJorge Luis Sanchez Solis
 
Bibliotecas ante el siglo XXI: nuevos medios y caminos
Bibliotecas ante el siglo XXI: nuevos medios y caminosBibliotecas ante el siglo XXI: nuevos medios y caminos
Bibliotecas ante el siglo XXI: nuevos medios y caminosJulián Marquina
 
Hoe veilig is het werken in een verontreinigde bodem(compleet)_K.Hoogeboom_16...
Hoe veilig is het werken in een verontreinigde bodem(compleet)_K.Hoogeboom_16...Hoe veilig is het werken in een verontreinigde bodem(compleet)_K.Hoogeboom_16...
Hoe veilig is het werken in een verontreinigde bodem(compleet)_K.Hoogeboom_16...Kelvin Hoogeboom
 
Como Hacer Una Encuesta
Como Hacer Una EncuestaComo Hacer Una Encuesta
Como Hacer Una EncuestaFabio Valencia
 

Viewers also liked (20)

Scala类型系统
Scala类型系统Scala类型系统
Scala类型系统
 
Scala
ScalaScala
Scala
 
Karl marx
Karl marxKarl marx
Karl marx
 
Woman In Islam _ Greek
Woman In Islam _ GreekWoman In Islam _ Greek
Woman In Islam _ Greek
 
Προσανατολισμός στο φυσικό και στο δομημένο περιβάλλον - ΚΠΕ Μακρινίτσας
Προσανατολισμός στο φυσικό και στο δομημένο περιβάλλον - ΚΠΕ ΜακρινίτσαςΠροσανατολισμός στο φυσικό και στο δομημένο περιβάλλον - ΚΠΕ Μακρινίτσας
Προσανατολισμός στο φυσικό και στο δομημένο περιβάλλον - ΚΠΕ Μακρινίτσας
 
Ucd火花集
Ucd火花集Ucd火花集
Ucd火花集
 
Comercio electrónico en méxico
Comercio electrónico en méxicoComercio electrónico en méxico
Comercio electrónico en méxico
 
Ucd火花集
Ucd火花集Ucd火花集
Ucd火花集
 
Beautiful Structure
Beautiful StructureBeautiful Structure
Beautiful Structure
 
INTENSIDAD,RESISTENCIA Y VOLTAJE
INTENSIDAD,RESISTENCIA Y VOLTAJEINTENSIDAD,RESISTENCIA Y VOLTAJE
INTENSIDAD,RESISTENCIA Y VOLTAJE
 
Enfermedades de transmisión sexual
Enfermedades de transmisión sexualEnfermedades de transmisión sexual
Enfermedades de transmisión sexual
 
PlAn De MeDiOs
PlAn De MeDiOsPlAn De MeDiOs
PlAn De MeDiOs
 
Luis felipe cadavid acosta
Luis felipe cadavid acostaLuis felipe cadavid acosta
Luis felipe cadavid acosta
 
Incorporación de la Gestión de Tecnología como un área del conocimiento en la...
Incorporación de la Gestión de Tecnología como un área del conocimiento en la...Incorporación de la Gestión de Tecnología como un área del conocimiento en la...
Incorporación de la Gestión de Tecnología como un área del conocimiento en la...
 
4a imagen reputacion_villafane
4a imagen reputacion_villafane4a imagen reputacion_villafane
4a imagen reputacion_villafane
 
Incoterms 2010
Incoterms 2010Incoterms 2010
Incoterms 2010
 
Preguntas y respuestas de economia capitulos 1 3 anexo ensayo
Preguntas y respuestas de economia capitulos 1 3 anexo ensayoPreguntas y respuestas de economia capitulos 1 3 anexo ensayo
Preguntas y respuestas de economia capitulos 1 3 anexo ensayo
 
Bibliotecas ante el siglo XXI: nuevos medios y caminos
Bibliotecas ante el siglo XXI: nuevos medios y caminosBibliotecas ante el siglo XXI: nuevos medios y caminos
Bibliotecas ante el siglo XXI: nuevos medios y caminos
 
Hoe veilig is het werken in een verontreinigde bodem(compleet)_K.Hoogeboom_16...
Hoe veilig is het werken in een verontreinigde bodem(compleet)_K.Hoogeboom_16...Hoe veilig is het werken in een verontreinigde bodem(compleet)_K.Hoogeboom_16...
Hoe veilig is het werken in een verontreinigde bodem(compleet)_K.Hoogeboom_16...
 
Como Hacer Una Encuesta
Como Hacer Una EncuestaComo Hacer Una Encuesta
Como Hacer Una Encuesta
 

Similar to 泛型总结

系統程式 -- 第 7 章 高階語言
系統程式 -- 第 7 章 高階語言系統程式 -- 第 7 章 高階語言
系統程式 -- 第 7 章 高階語言鍾誠 陳鍾誠
 
Compiler for Dummy 一點都不深入的了解 Compiler, Interpreter 和 VM
Compiler for Dummy 一點都不深入的了解 Compiler, Interpreter 和 VMCompiler for Dummy 一點都不深入的了解 Compiler, Interpreter 和 VM
Compiler for Dummy 一點都不深入的了解 Compiler, Interpreter 和 VMLi Hsuan Hung
 
系統程式 -- 第 8 章 編譯器
系統程式 -- 第 8 章 編譯器系統程式 -- 第 8 章 編譯器
系統程式 -- 第 8 章 編譯器鍾誠 陳鍾誠
 
Erlang培训
Erlang培训Erlang培训
Erlang培训liu qiang
 
Swift编程语言入门教程 中文版
Swift编程语言入门教程 中文版Swift编程语言入门教程 中文版
Swift编程语言入门教程 中文版Harvey Zhang
 
组件交互模式的非主流研究
组件交互模式的非主流研究组件交互模式的非主流研究
组件交互模式的非主流研究youalab
 
Coding guideline
Coding guidelineCoding guideline
Coding guideline斯理 衛
 
Java Collections中的Fail Fast机制
Java Collections中的Fail Fast机制Java Collections中的Fail Fast机制
Java Collections中的Fail Fast机制yiditushe
 
Jsp面试知识
Jsp面试知识Jsp面试知识
Jsp面试知识yiditushe
 
Arrays的Sort算法分析
Arrays的Sort算法分析Arrays的Sort算法分析
Arrays的Sort算法分析Zianed Hou
 
Java 面试32问
Java 面试32问Java 面试32问
Java 面试32问yiditushe
 
Jquery指南
Jquery指南Jquery指南
Jquery指南yiditushe
 
GTest交流与经验总结
GTest交流与经验总结GTest交流与经验总结
GTest交流与经验总结coderzh
 
J2ee面试知识
J2ee面试知识J2ee面试知识
J2ee面试知识yiditushe
 
第01章 绪论(java版)
第01章  绪论(java版)第01章  绪论(java版)
第01章 绪论(java版)Yan Li
 
Java script closures
Java script closuresJava script closures
Java script closuresskywalker1114
 

Similar to 泛型总结 (20)

SCJP ch17
SCJP ch17SCJP ch17
SCJP ch17
 
第二章
第二章第二章
第二章
 
系統程式 -- 第 7 章 高階語言
系統程式 -- 第 7 章 高階語言系統程式 -- 第 7 章 高階語言
系統程式 -- 第 7 章 高階語言
 
Compiler for Dummy 一點都不深入的了解 Compiler, Interpreter 和 VM
Compiler for Dummy 一點都不深入的了解 Compiler, Interpreter 和 VMCompiler for Dummy 一點都不深入的了解 Compiler, Interpreter 和 VM
Compiler for Dummy 一點都不深入的了解 Compiler, Interpreter 和 VM
 
系統程式 -- 第 8 章 編譯器
系統程式 -- 第 8 章 編譯器系統程式 -- 第 8 章 編譯器
系統程式 -- 第 8 章 編譯器
 
Erlang培训
Erlang培训Erlang培训
Erlang培训
 
Swift编程语言入门教程 中文版
Swift编程语言入门教程 中文版Swift编程语言入门教程 中文版
Swift编程语言入门教程 中文版
 
组件交互模式的非主流研究
组件交互模式的非主流研究组件交互模式的非主流研究
组件交互模式的非主流研究
 
Coding guideline
Coding guidelineCoding guideline
Coding guideline
 
Java Collections中的Fail Fast机制
Java Collections中的Fail Fast机制Java Collections中的Fail Fast机制
Java Collections中的Fail Fast机制
 
Jsp面试知识
Jsp面试知识Jsp面试知识
Jsp面试知识
 
Arrays的Sort算法分析
Arrays的Sort算法分析Arrays的Sort算法分析
Arrays的Sort算法分析
 
Java 面试32问
Java 面试32问Java 面试32问
Java 面试32问
 
Jquery指南
Jquery指南Jquery指南
Jquery指南
 
GTest交流与经验总结
GTest交流与经验总结GTest交流与经验总结
GTest交流与经验总结
 
J2ee面试知识
J2ee面试知识J2ee面试知识
J2ee面试知识
 
第01章 绪论(java版)
第01章  绪论(java版)第01章  绪论(java版)
第01章 绪论(java版)
 
getPDF.aspx
getPDF.aspxgetPDF.aspx
getPDF.aspx
 
getPDF.aspx
getPDF.aspxgetPDF.aspx
getPDF.aspx
 
Java script closures
Java script closuresJava script closures
Java script closures
 

More from wang hongjiang

Shell,信号量以及java进程的退出
Shell,信号量以及java进程的退出Shell,信号量以及java进程的退出
Shell,信号量以及java进程的退出wang hongjiang
 
中等创业公司后端技术选型
中等创业公司后端技术选型中等创业公司后端技术选型
中等创业公司后端技术选型wang hongjiang
 
Scala function-and-closures
Scala function-and-closuresScala function-and-closures
Scala function-and-closureswang hongjiang
 
Java7 fork join framework and closures
Java7 fork join framework and closuresJava7 fork join framework and closures
Java7 fork join framework and closureswang hongjiang
 
Exodus重构和向apollo迁移
Exodus重构和向apollo迁移Exodus重构和向apollo迁移
Exodus重构和向apollo迁移wang hongjiang
 
Effective linux.3.(diagnosis)
Effective linux.3.(diagnosis)Effective linux.3.(diagnosis)
Effective linux.3.(diagnosis)wang hongjiang
 
Effective linux.2.(tools)
Effective linux.2.(tools)Effective linux.2.(tools)
Effective linux.2.(tools)wang hongjiang
 
Effective linux.1.(commandline)
Effective linux.1.(commandline)Effective linux.1.(commandline)
Effective linux.1.(commandline)wang hongjiang
 

More from wang hongjiang (15)

Shell,信号量以及java进程的退出
Shell,信号量以及java进程的退出Shell,信号量以及java进程的退出
Shell,信号量以及java进程的退出
 
中等创业公司后端技术选型
中等创业公司后端技术选型中等创业公司后端技术选型
中等创业公司后端技术选型
 
Ali-tomcat
Ali-tomcatAli-tomcat
Ali-tomcat
 
functional-scala
functional-scalafunctional-scala
functional-scala
 
Scala function-and-closures
Scala function-and-closuresScala function-and-closures
Scala function-and-closures
 
Jvm内存管理基础
Jvm内存管理基础Jvm内存管理基础
Jvm内存管理基础
 
聊一些电影
聊一些电影聊一些电影
聊一些电影
 
Java7 fork join framework and closures
Java7 fork join framework and closuresJava7 fork join framework and closures
Java7 fork join framework and closures
 
善用工具
善用工具善用工具
善用工具
 
Aswan&hump
Aswan&humpAswan&hump
Aswan&hump
 
Exodus重构和向apollo迁移
Exodus重构和向apollo迁移Exodus重构和向apollo迁移
Exodus重构和向apollo迁移
 
Exodus2 大局观
Exodus2 大局观Exodus2 大局观
Exodus2 大局观
 
Effective linux.3.(diagnosis)
Effective linux.3.(diagnosis)Effective linux.3.(diagnosis)
Effective linux.3.(diagnosis)
 
Effective linux.2.(tools)
Effective linux.2.(tools)Effective linux.2.(tools)
Effective linux.2.(tools)
 
Effective linux.1.(commandline)
Effective linux.1.(commandline)Effective linux.1.(commandline)
Effective linux.1.(commandline)
 

泛型总结

  • 1. 几年前当 Java5 还未正式发布的时候,看到过一些人写的介绍 Tiger 中的新特性,当时对 我第一感觉冲击最大的就是泛型(generics)和注释(annotation),因为它们直接影响了我 们编码的语法习惯。 在后来的使用过程中,对于泛型一直没有特别深入的使用过,没有遇到那样的需求和场景。 只需要了解 Java 中的泛型是编译期的,运行期被“擦拭”掉了;然后还有几种通配符的表 示就足够了。 直到一天我在查看 Java5 中 Enum 的源代码时,发现它是这么定义的: public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable { 这个类似递归结构的 Enum<E extends Enum<E>> 究竟表达什么意思? 随后我又看到了在Collections工具类中的max 方法: public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) { 怎么TMD还会有这么复杂的泛型表达式!? (幸好的是这种情况在我们实际开发过程中不多见,甚至不应该见到,大概只有像JDK这种 为了保留对老版本的兼容才会设计出这么复杂的泛型表达式出来。) 上面的问题,你可以通过:http://java.sun.com/j2se/1.5/pdf/generics-tutorial.pdf 来获取答案,中文版的在: http://blog.csdn.net/explorers/archive/2005/08/15/454837.aspx 这篇泛型指南的文章,非常详细。 或许你看了上面的文章,依然心存疑虑。我下面的内容则是对这篇文档的一些补充(会和这 篇文档有点重合)。 在回到我之前抛出的问题上: public static void foo(List<? extends Number> l){ l.add(new Integer(2)); // 编译通过么? Why ? } public static void bar(List<? super Number> l){ l.add(new Integer(2)); // 编译通过么? Why ? l.add(new Float(2)); // ok? } 这里主要说说 <? extends T> 和 <? super T> 这两种通配符对于方法参数的使用原则。 即 PECS 原则 (producser-extends, consumer-super) 或者也叫 Get and Put 原则 当没有使用通配符的情况下,我们定义一个方法:
  • 2. public static <E> void test(List<E> l){ E e = l.get(0); l.set(0, e); } 我们从List中 get和set都没有问题,因为这个E 它的类型是某种明确的类型。 而当使用通配符时来描述参数时,就有些不同了。 我们先定义一下两种通配符: <? extends E> 是 Upper Bound(上限) 的通配符 <? super E> 是 Lower Bound(下限) 的通配符 1) 当使用 Upper Bound 通配符时: public static void test(List<?> list){ Object e = list.get(0); // get OK list.set(0, e); // set 编译报错 } 上面代码中通配符<?> 是 <? extends Object> 的简写。(关于<?>是否和<? extends Object>完全等价,我遇到个小插曲,在结束的时候来描述) 在eclipse里错误提示为: The method set(int, capture#2-of ?) in the type List<capture#2-of ?> is not applicable for the arguments (int, Object) 注: <capture#2-of ?> 是一个占位符,表示编译器对通配符的捕获,更多见: http://www.ibm.com/developerworks/cn/java/j-jtp04298.html set报错的原因是因为此时方法中的类型是不可具体化的(reified),你可以传递一个 String,Number,Book,等任何继承自Object的类作为List的参数类型给test方法,而 list要求集合中的类型必须是一致的,set的时候没有办法保证set进去的数据类型是否和 list中原本的类型一致,比如你传给test方法的是 List<Book>, 那么在方法中set进去一 个Object显然类型就不一致了。 这也是通配符带来灵活性的同时所要付出的代价。 结论:使用了 <? extends E> 这样的通配符,test方法的参数list变成了只能get不能 set(除了null) 或者不严谨的说它变成了只读参数了, 有些类似一个生产者,提供数据。 2) 当使用 Lower Bound 的通配符时: public static void test(List<? super Number> list){ Number n = list.get(0); // 编译错误 Object o = list.get(0); // OK list.set(0, new Object()); // 编译错误 list.set(0, new Long(0)); // OK list.set(0, new Integer(0)); // OK }
  • 3. 这时get只能get出最宽泛的父类型,即Object。 这时set的时候,必须是Number或Number的子类。 原因和上面的get类似。 结论: 使用了<? super E> 这种通配符,test方法的参数list的get受到了很大的制约, 只能最宽泛的方式来获取list中的数据,相当于get只提供了数据最小级别的访问权限(想 想,你可能原本是放进去了一个Book,却只能当作Object来访问)。 它更多适合于set的使用场景,像是一个消费者,主要用来消费数据。 上面便是对通配符的使用原则的说明,简单的说 PECS原则是指导我们在泛型方法中使用通 配符的直接原则。参数作为生产者使用<? extends E>,作为消费者时使用<? super E> 。 那么说完了 PECS原则,我们再回过头来分析那两个复杂的泛型表达式是怎么含义 1) class Enum<E extends Enum<E>> 它确实是一个 “递归类型限制”(recursive type bound) 要说明白这个问题,我们要先明白2点: a) Enum可以理解为一组同一类型的有限的数据集合; b) Java对Enum中的数据类型要求必须也是枚举类型(即必须是继承Enum类的)。 对于 a) 我们先定义一个 Enum<T> 表明定义了T这种类型作为它内部的数据类型,这么看 它就像个普通的集合。 再来根据b) 定义类型E,要求E必须是继承自 Enum<T>,便成了 <E extends Enum<T>> 实际上 E和T是一回事,它们是同一类型, 所以它就是 <E extends Enum<E>> 暂停,可能我上面的表达不太合理可能会误人子弟,递归类型限制是有些抽象,它应该有 严谨的数学描述,我想不清楚怎么表达,我先用另一个简单些例子来说明吧 public static <T extends Comparable<T>> T max(List<T> list) 这个方法用来获取list 中的最大值,它定义了一个 <T extends Comparable<T>> 的 类型表达式 这个递归类型限制的表达式容易理解一些, <T extends Comparable<T>> 表示的是:针对可以与自身进行比较的每个类型 T。 或者说,每个实现了Comparable<T>接口的类型T,比如 String 实现了 Comparable<String> , Long实现了Comparable<Long>等等 而Enum因为使用enum关键字的原因,让我们忽略了它底层的实现其实也是 class EnumSample extends Enum<EnumSample> 这一事实, 比如 我们定义了 BoundKind 这样的一个枚举: public enum BoundKind{ } 编译器会转换为: public final class BoundKind extends java.lang.Enum<BoundKind>
  • 4. 看到了,这和 String implements Comparable<String> 类似 这样我们套回到<E extends Enum<E>> 就是 <BoundKind extends Enum<BoundKind>> 这下好理解了, <E extends Enum<E>> 直接按字面理解:每个继承自Enum<E>的类型 E,比如 BoundKind 继承了 Enum<BoundKind> 通过与<T extends Comparable<T>>的对比,我们可以理解 <E extends Enum<E>>了。 那现在我们再回到Collections工具类中的max 方法: public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) { 我们先简化一下这个表达式,看看 <T extends Comparable<? super T>> 怎么理解? 既然 <T extends Comparable<T>> 我们都理解了,把Comparable<T> 改为 Comparable<? super T> 也没什么费解的 在《Java1.5 Generics Tutorial》一文中的解释是: T 精确的(exactly)和自己能比较是不需要的。所需要的 是 T能 够和它的父类中的一个 进行比较。 而《Effictive Java》第二版中对此是用 PECS原则来解释的: 下面是修改过的使用通配符类型的声明: public static <T extends Comparable<? super T>> T max(List<? extends T> list) 为了从初始声明中得到修改后的版本,要应用PECS转换两次,最直接的是运用到参数 List。它产生T实例,因此将类型从List<T>改为List<? extends T>。(ok好理解) 。。。 。。 。 更灵活的是运用到类型参数T。最初T 被指定用来扩展Comparable<T>,但是T的 comparable消费T实例(并产生表示顺序关系的整值)。因此,参数化类型 Comparable<T>被有限制通配符类型Comparable<? super T>取代。 comparable始终 是消费者,因此使用时始终应该是Comparable<? super T> 优先于 Comparable<T>。 蓝色粗体的那句翻译的不好。还是看一下代码来理解吧: public static <T extends Comparable<? super T>> T max(Collection<? extends T> coll) { 1 Iterator<? extends T> i = coll.iterator(); 2 T candidate = i.next(); 3 while (i.hasNext()) { 4 T next = i.next(); 5 if (next.compareTo(candidate) > 0) // here comparaTo 6 candidate = next;
  • 5. 7 } 8 return candidate; 9 } 第5行,Bloch认为 next.compareTo(cand) 是一句消费操作,在消费一个candidate对象时, 根据PECS原则,candidate的类型应该使用 <? super T> 来提高它的灵活性。 我觉得Bloch将第5行当作消费操作挺别扭的,我个人偏向《Java1.5 Generics Tutorial》中 的解释。 但归根到底,都是降低限制,提高比较时的灵活性。 最后,我们再来完整的理解:<T extends Object & Comparable<? super T>> 就只是比 <T extends Comparable<? super T>> 多了一个限制(bounds)。 Object & Comparable<? super T> 是一个多限定(multiple bounds)的用法, 语法为: T1 & T2 … & Tn 一个有多个界限的类型的参数是所有界限中列出来的类型的子类。 当多个界限被使用的时候, 界限中的第一个类型被用作这个类型参数的erasure。 最终这个方法的返回值,按照第一个限定,擦拭为Object类型了。这是因为在以前版本中此 方法就是返回的Object类型,需要兼容。 因为多限定(multiple bounds)的存在,泛型方法中又对应的增加了一个很不优雅的调用 方式。下面用一段代码来说明: public class GenericsTest { static class Book {}; static class StoryBook extends Book implements Comparable<StoryBook> { @Override public int compareTo(StoryBook o) { return 0; //FIXME }}; static class TechBook extends Book implements Comparable<TechBook> { @Override public int compareTo(TechBook o) { return 0; //FIXME }}; public static <E> Set<E> merge(Set<? extends E> s1, Set<? extends E> s2) { HashSet<E> newSet = new HashSet<E>(s1); newSet.addAll(s2);
  • 6. return newSet; } public static void main(String[] args) { HashSet<StoryBook> s1 = new HashSet<StoryBook>(); HashSet<TechBook> s2 = new HashSet<TechBook>(); Set<Book> sb = merge(s1, s2); // 错误 // 需通过显式的类型参数(explicit type parameter)来告诉它要使用哪种 类型 Set<Book> bs = GenericsTest.<Book>merge(s1,s2); //OK // 或者 Set<Comparable<?>> s = GenericsTest.<Comparable<? >>merge(s1,s2); } } 上面直接调用merge(s1,s2) 那行代码错误的提示信息: Type mismatch: cannot convert from Set<GenericsTest.Book&Comparable<?>> to Set<GenericsTest.Book> 这归于泛型的类型推导(type inference),当无法推导出明确的类型时,就需要显式的描 述,如上面代码中红色粗体字。 后注: 有关于 <?> 与 <? extends Object>是否是一回事 今天中午发现同事桌上有本 《Java编程思想》 第四版,随手翻了一下,发现泛型一章的介绍 中,有句描述:“UnboundedWildcards.java 展示了编译器处理List<?>和List<? Extends Object>时是不同的。” 这让我奇怪,查看了一下它的代码,主要因为是对于Raw类型的造型为泛型时的警告信息不 同。 将一个Raw的ArrayList造型给 List<?> 没有问题,而给List<? Extends Object>却会有警 告。 在网上查了一下,发现对于<?>与<? extends Object>是否等同,是有些不同意见 的。 http://mail.openjdk.java.net/pipermail/compiler-dev/2008-April/000316.html http://bugs.sun.com/view_bug.do?bug_id=6559175 这个报告里,有两段代码反映了两者的不同: (1) public static void main(String[] args) { Object customer = null; foo((List<? extends String>) customer ); //[1] foo((List<? extends Object>) customer ); //[2] 编译有警告 foo((List<?>) customer ); //[3] 编译没有警告
  • 7. } public static void foo(List<?> list) { } (2) Object o2 = new List<?>[3]; // 编译居然OK,估计直接当作Raw处理了 Object o3 = new List<? extends Object>[3]; // 报错 上面两段代码,表明了当与Raw类型造型时,<?>在编译器的处理方式的确与<? Extends Object>有所不同,根据场景它可能被编译器忽略掉泛型信息而直接当作Raw类型,而<? Extends Object>则不会。 但这种差异,有些吹毛求疵,除了跟Raw类型转换方面存在差异,在语义上两者可以认为是 完全等同的,见:http://bugs.sun.com/view_bug.do?bug_id=6480391 The introduction of the capture conversion simplified a lot of things. One of the things it did is make "?" equivalent to "? extends Object". Unfortunately, JLS3 doesn't say they are equivalent. SUN 的开发人员回复说: ? should be considered equivalent to ? extends Object. I will note this at the end of the text about bounds for wildcards in 4.5.1. …… Hence, Foo<?> is semantically equivalent to Foo<? extends Object> 但查了一下发现目前 JLS3中还依然没有增加他说要加的那句注释,见: http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.5.1 我们暂从语义上认为两者相等。