Let G name a generic type declaration with n type parameters A1,...,An with corresponding bounds U1,...,Un.
 
There exists a capture conversion from a parameterized type G<T1,...,Tn>  to a parameterized type G<S1,...,Sn>, where, for 1 ≤ i ≤ n :
 

(1)If Ti is a wildcard type argument of the form ?, then Si is a fresh type variable whose upper bound is Ui[A1:=S1,...,An:=Sn] and whose lower bound is the null type.

  1. import java.io.BufferedInputStream;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4.  
  5. public class CaptureExampleUnboundedWildcard<E extends InputStream> {
  6. public void readFromIt(E readFrom) throws IOException{
  7. readFrom.read();
  8. }
  9.  
  10. public E getSomething(){
  11. return null;
  12. }
  13.  
  14. public static void main(String [] args) throws Exception{
  15. CaptureExampleUnboundedWildcard<?> ce=new CaptureExampleUnboundedWildcard<BufferedInputStream>();
  16.  
  17. BufferedInputStream obj=new BufferedInputStream(null);
  18. InputStream i = ce.getSomething(); // ok
  19. ce.readFromIt(obj); // compiler-time error
  20. }
  21. }

这里的CaptureExampleUnboundedWildcard就是G,A1,...An就是<E extends InputStream>,相对应的上/下界声明(U1,..,Un)就是InputStream。

G<T1,...,Tn>是这里的CaptureExampleUnboundedWildcard<?>(在英文中叫a parameterized type,中文译为一个参数化的类型),在调用readFromIt()方法时就需要capture conversion了,也就是为方法中的E推断出G<S1,...,Sn>。

调用readFromIt()方法会报错,Eclipse的提示如下:

  1. The method readFromIt(capture#1-of ?) in the type CaptureExampleUnboundedWildcard<capture#1-of ?> is not applicable for the arguments (BufferedInputStream)

其中有对Ui[A1:=S1,...,An:=Sn]这个表达式的理解。参考阅读:

(1)http://docs.oracle.com/javase/specs/jls/se8/html/jls-1.html#jls-1.3

(2)http://stackoverflow.com/questions/31206947/understanding-captured-conversion

也就是将声明的A1,...An用S1,...Sn来代替,并且新的这个S1,..,Sn的上界为Ui[A1:=S1,...,An:=Sn]。那么如上的例子中ce.readFromIt()方法调用中,实际的类型在new CaptureExampleUnboundedWildcard 时就已经确定为BufferedInputStream,可惜编译器并不保持这个类型变量,而是通过参数化类型声明CaptureExampleUnboundedWildcard<?>和类定义时的类型参数声明CaptureExampleUnboundedWildcard<E extends InputStream>来推断。推断后编译器只能知道新变量的上界为InputStream,具体是InputStream中什么具体的子类型,编译器并不知道,也就不允许放入BufferedInputStream了。

假如在如上类中添加一个方法,如下:

  1. public <X extends InputStream> void readFromIt2(X x) throws IOException{
  2. x.read();
  3. }

调用如上方法然后传入一个BufferedInputStream的实例后,能够正常调用,因为编译器马上推断出了X类型变量的类型为BufferedInputStream,并且满足上界约束条件。

(2)If Ti is a wildcard type argument of the form ? extends Bi, then Si is a fresh type variable whose upper bound is glb(Bi, Ui[A1:=S1,...,An:=Sn]) and whose lower bound is the null type.

glb(V1,...,Vm) is defined as V1 & ... & Vm. 

  1. public class CaptureExampleUpperBoundedWildcard<E extends InputStream> {
  2. public void readFromIt(E readFrom) throws IOException{
  3. readFrom.read();
  4. }
  5.  
  6. public static void main(String [] args) throws Exception{
  7. //The ce is passed only ? extends Serializable as a type argument. Hence the capture converted type has an upper
  8. //bound same as the original bound during the declaration, which is InputStream
  9. //Note that it is not possible to create an Object of wildcard type. That is a compile
  10. //time error
  11.  
  12. CaptureExampleUpperBoundedWildcard<? extends BufferedInputStream> ce=new CaptureExampleUpperBoundedWildcard<BufferedInputStream>();
  13.  
  14. }
  15. }

It is a compile-time error if, for any two classes (not interfaces) Vi and Vj, Vi is not a subclass of Vj or vice versa.

这里BufferedInputStream是InputStream类的子类,否则会出现编译错误。

(3)If Ti is a wildcard type argument of the form ? super Bi, then Si is a fresh type variable whose upper bound is Ui[A1:=S1,...,An:=Sn] and whose lower bound is Bi.

  1. import java.io.BufferedInputStream;
  2. import java.io.FileInputStream;
  3. import java.io.FilterInputStream;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6.  
  7. public class CaptureExampleLowerBoundedWildcard<E extends InputStream> {
  8. public void readFromIt(E readFrom) throws IOException {
  9. readFrom.read();
  10. }
  11.  
  12. public E getSomething(){
  13. return null;
  14. }
  15.  
  16. public static void main(String[] args) throws Exception {
  17. CaptureExampleLowerBoundedWildcard<? super FilterInputStream> ce = new CaptureExampleLowerBoundedWildcard<InputStream>();
  18.  
  19. BufferedInputStream obj = new BufferedInputStream(new FileInputStream("somefile"));
  20.  
  21. // Note that subclasses of the lower bound are allowed in method
  22. // invocation due normal method invocation conversion
  23. ce.readFromIt(obj); // ok
  24.  
  25. InputStream b = ce.getSomething(); // ok
  26.  
  27. }
  28. }

通过a parameterized type,也就是CaptureExampleLowerBoundedWildcard<? super FilterInputStream>与类定义时的类型参数声明CaptureExampleLowerBoundedWildcard<E extends InputStream>可知,新的类型变量下界为FilterInputStream,而上界为InputStream。那么满足这个条件的都可以,在new CaptureExampleLowerBoundedWildcard时传递了InputStream和FilterInputStream,而不能使用BufferedInputStream。

值得注意的是,ce.readFromIt(obj)中,这个obj是bufferedInputStream,也就是FilterInputStream的直接子类,按理说不满足如上的上下界,这是由于发生了method invocation conversion,具体就是a widening reference conversion转换,导致obj也满足了限定条件。 

 

当Ti为? super Bi,那么Bi应该为Ai的子类,举例如下:
 
  1. interface IA {}
  2.  
  3. class B implements IA {}
  4.  
  5. class Test6<T extends IA> {
  6. public void test() {
  7. Test6<? super B> x = new Test6<B>(); // B不实现IA会报错
  8. }
  9. }

(4)Otherwise, Si = Ti

  1. public class CaptureExample<E, F extends E> {
  2. public void copy(List<E> toGet, List<F> copyFrom){
  3. for(F x:copyFrom){
  4. toGet.add(x);
  5. }
  6. }
  7.  
  8. public static void main(String [] args){
  9. List<Object> aList=new ArrayList<Object>();
  10. List<String> copyFrom=new ArrayList<String>();
  11. copyFrom.add("Hi");
  12. CaptureExample<Object, String> ce=new CaptureExample<Object, String>();//Here is where the capture conversion takes place
  13. ce.copy(aList, copyFrom);
  14.  
  15. System.out.println(aList);
  16.  
  17. }
  18. }

 

重新解读通配符:

e.g 1

  1. public class CaptureExampleUnboundedWildcard<E extends InputStream> {
  2.  
  3. // 方法形式参数表示设置值
  4. public void set(E readFrom) throws IOException{
  5. readFrom.read();
  6. }
  7.  
  8. // 返回值为类型参数,表示取值
  9. public E get(){
  10. return null;
  11. }
  12.  
  13. public static void main(String [] args) throws Exception{
  14.  
  15. CaptureExampleUnboundedWildcard<?> ce = new CaptureExampleUnboundedWildcard<InputStream>();
  16.  
  17. //The method set(capture#1-of ?) in the type CaptureExampleUnboundedWildcard<capture#1-of ?>
  18. //is not applicable for the arguments (BufferedInputStream)
  19. BufferedInputStream bis = null;
  20. ce.set(bis);
  21.  
  22. //The method set(capture#2-of ?) in the type CaptureExampleUnboundedWildcard<capture#2-of ?>
  23. //is not applicable for the arguments (InputStream)
  24. InputStream is = null;
  25. ce.set(is);
  26.  
  27. ce.set(null);
  28.  
  29. Object o1 = ce.get();
  30. InputStream o2 = ce.get();
  31. // Type mismatch: cannot convert from capture#6-of ? to FilterInputStream
  32. FilterInputStream o3 = ce.get();
  33. }
  34. }

从上面的实例可以看出,通配符为?时,只能设置null值,但是可以取值。

e.g 2

  1. interface IA{}
  2. class A{}
  3. class B{}
  4.  
  5. public class CaptureExampleUpperBoundedWildcard<E extends A> {
  6.  
  7. // 方法形式参数表示设置值
  8. public void set(E readFrom) throws IOException{
  9.  
  10. }
  11.  
  12. // 返回值为类型参数,表示取值
  13. public E get(){
  14. return null;
  15. }
  16.  
  17. public static void main(String [] args) throws Exception{
  18.  
  19. //Bound mismatch: The type ? extends B is not a valid substitute for the bounded parameter <E extends A>
  20. //of the type CaptureExampleUpperBoundedWildcard<E>
  21. //CaptureExampleUpperBoundedWildcard<? extends IA> ce = new CaptureExampleUpperBoundedWildcard();
  22.  
  23. CaptureExampleUpperBoundedWildcard<? extends IA> ce = new CaptureExampleUpperBoundedWildcard();
  24.  
  25. // 当为<? extends Serializable>时不能存入任何元素,除了null值
  26. ce.set(null); // 在方法的参数中就相当于给设置值,而在方法的返回中就相当于获取值
  27.  
  28. Object o1 = ce.get();
  29. A o2 = ce.get();
  30. IA o3 = ce.get();
  31. }
  32.  
  33. }

从上面的实例可以看出,通配符为? extends X时,只能设置null值,但是可以取值。

声明中的E extends Y 与 通配符声明 ? extends X之间的关系如下:

(1)X与Y有一个为接口时,则相互之间没有限制。

(2)当X与Y都为类时,两个类之间必须有父子关系。抽象类也看作类

e.g 3

  1. class SubBufferedInputStream extends BufferedInputStream{
  2. public SubBufferedInputStream(InputStream in) {
  3. super(in);
  4. }
  5. }
  6. public class CaptureExampleLowerBoundedWildcard<E extends FilterInputStream> {
  7.  
  8. // 方法形式参数表示设置值
  9. public void set(E readFrom) throws IOException{
  10. readFrom.read();
  11. }
  12.  
  13. // 返回值为类型参数,表示取值
  14. public E get(){
  15. return null;
  16. }
  17.  
  18. public static void main(String [] args) throws Exception{
  19.  
  20. CaptureExampleLowerBoundedWildcard<? super BufferedInputStream> ce=new CaptureExampleLowerBoundedWildcard<FilterInputStream>();
  21.  
  22. //Bound mismatch: The type ? super InputStream is not a valid substitute for
  23. //the bounded parameter <E extends FilterInputStream> of the type CaptureExampleLowerBoundedWildcard<E>
  24. // CaptureExampleLowerBoundedWildcard<? super InputStream> ce = new CaptureExampleLowerBoundedWildcard<FilterInputStream>();
  25.  
  26. //Now we can call this without an error, because the capture converted type does have a lower bound.
  27. //Note that subclasses of the lower bound are allowed in method invocation due normal method invocation conversion
  28. BufferedInputStream obj= null;
  29. ce.set(obj);
  30.  
  31. SubBufferedInputStream sis = null;
  32. ce.set(sis);
  33.  
  34. //The method set(capture#3-of ? super BufferedInputStream) in the type
  35. //CaptureExampleLowerBoundedWildcard<capture#3-of ? super BufferedInputStream> is
  36. //not applicable for the arguments (FilterInputStream)
  37. FilterInputStream fis = null;
  38. ce.set(fis); // error
  39.  
  40. FilterInputStream o1 = ce.get();
  41. // Type mismatch: cannot convert from capture#5-of ? super BufferedInputStream to SubBufferedInputStream
  42. SubBufferedInputStream o2 = ce.get(); // error
  43. // Type mismatch: cannot convert from capture#6-of ? super BufferedInputStream to BufferedInputStream
  44. BufferedInputStream o3 = ce.get(); // error
  45.  
  46. }
  47.  
  48. }

从上面的实例可以看出,通配符为? super X时,可以设置X及其子类,取值时能接收的最精确的类型为声明时E extends Y的Y类型。

X与Y不管是接口还是类必须要形成父子关系。

当声明为E,而通配符为?,? extends X或者? super X时,会是什么情况呢?也就是? extends X或者 ? super X时的类型X无限制。取值时只能以Object来接收。

 

参考文献:

(1)http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1

(2)http://www.geekyarticles.com/2011/11/java-generics-capture-conversion.html

(3)https://www.ibm.com/developerworks/java/library/j-jtp04298/index.html

Capture Conversion解读的更多相关文章

  1. Thinking in Java——笔记(15)

    Generics The term "generic" means "pertaining or appropriate to large groups of class ...

  2. java泛型小问题

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

  3. javac后期需要重点阅读的类

    (1)Annotate (300行) Enter annotations on symbols. Annotations accumulate in a queue,which is processe ...

  4. javac的泛型

    ?:在实例化对象的时候,不确定泛型参数的具体类型时,可以使用通配符进行对象定义. (1)?表示通配符,通配符 与 T 的区别 T:作用于模板上,用于将数据类型进行参数化,不能用于实例化对象. publ ...

  5. Javac之glb与lub

    5.1.10. Capture Conversion Let G name a generic type declaration (§8.1.2, §9.1.2) with n type parame ...

  6. Types方法之isSameType-isSuperType-isSubType

    4.isSameType() 方法 /** * Is t the same type as s? */ public boolean isSameType(Type t, Type s) { retu ...

  7. Types方法之isCastable-isConvertible

    5. Conversions and Promotions 5.1. Kinds of Conversion 5.1.1. Identity Conversion 5.1.2. Widening Pr ...

  8. Android MVP模式 谷歌官方代码解读

    Google官方MVP Sample代码解读 关于Android程序的构架, 当前(2016.10)最流行的模式即为MVP模式, Google官方提供了Sample代码来展示这种模式的用法. Repo ...

  9. 2014百度之星资格赛 1001:Energy Conversion(水题,逻辑题)

    Energy Conversion Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others ...

随机推荐

  1. Angular4 配置问题

    出现错误: Local workspace file ('angular.json') could not be found.Error: Local workspace file ('angular ...

  2. C++的一些知识点摘抄(创建基本类 高级类)

    问:对象有多大? 答:对象占据的内存量取决于其成员变量的长度,类函数不占据为对象分配的内容. 有些编译器在内存中对齐变量,这导致2字节变量实际占用的内存多于2字节. 问:为何不应将所有成员数据声明为公 ...

  3. C#中Cookies的读取

    C#中Cookies的读取   链接: 一 .写入Cookie 1. Name 和 Value 属性由程序设定,默认值都是空引用. 2. Domain属性的默认值为当前URL的域名部分,不管发出这个c ...

  4. sharepoint 2013 升级要求

    1. 安装过程合理: A. 可以同时在管理中心.两台前端.搜索服务器上安装重新发布的SP1补丁包(所提供的链接) B. 等待所有SP1补丁包安装完成,依次在管理中心.两台前端.搜索服务器上运行配置向导 ...

  5. @Configurable

    spring的一个注解,用来自动注入bean的注解,不需要通过BeanFactory去获取    

  6. pod-infrastructure:latest镜像下载失败

    报错一:image pull failed for registry.access.redhat.com/rhel7/pod-infrastructure:latest, this may be be ...

  7. LOJ#6360. 复燃「恋之埋火」(最小圆覆盖+高斯消元)

    题面 传送门 题解 不难发现最小圆覆盖的随机增量法复杂度还是正确的 所以现在唯一的问题就是给定若干个点如何求一个\(m\)维的圆 其实就是这一题 //minamoto #include<bits ...

  8. 记一名软件实施自学转Java开发,附学习计划

    2015年毕业到现在已经3年了,而我转型开发已经有一年的时间了.写这篇文章除了记录,主要还是想分享一些经历给想要转型开发的同学们,不要走那些我走过的弯路. 2015年入职了第一家公司,当时是做的分销系 ...

  9. JavaScript基础总纲

    如果前人种好了树那我们干嘛不去享受阴凉,然后花费时间去为大树的成长进一份力. 我发现一个站点写的很全面写很系统,我总结主要分为一些几个模块: 一,JavaScript 教程(基础) 二,JavaScr ...

  10. Ionic2 启动加载优化总结

    1. ionic2通过ionic serve生成的main.js大于4M,必须先build才能部署 npm run ionic:build --prod 之后main.js缩小为大概100K+ 2. ...