几年前当 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
我们暂从语义上认为两者相等。

泛型总结

  • 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类似。 结论: 使用了<? superE> 这种通配符,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 implementsComparable<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 我们暂从语义上认为两者相等。