Capture Conversion解读

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.

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;

public class CaptureExampleUnboundedWildcard<E extends InputStream> {
    public void readFromIt(E readFrom) throws IOException{
        readFrom.read();
    }
     
    public E getSomething(){
        return null;
    }
     
    public static void main(String [] args) throws Exception{
        CaptureExampleUnboundedWildcard<?> ce=new CaptureExampleUnboundedWildcard<BufferedInputStream>();
         
        BufferedInputStream obj=new BufferedInputStream(null);
        InputStream i = ce.getSomething();   // ok
        ce.readFromIt(obj);  // compiler-time error
    }
}

这里的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的提示如下:

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了。

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

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

 

 

调用如上方法然后传入一个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.  
 
public class CaptureExampleUpperBoundedWildcard<E extends InputStream> {
    public void readFromIt(E readFrom) throws IOException{
        readFrom.read();
    }
    
    public static void main(String [] args) throws Exception{
        //The ce is passed only ? extends Serializable as a type argument. Hence the capture converted type has an upper
        //bound same as the original bound during the declaration, which is InputStream
        //Note that it is not possible to create an Object of wildcard type. That is a compile
        //time error

        CaptureExampleUpperBoundedWildcard<? extends BufferedInputStream> ce=new CaptureExampleUpperBoundedWildcard<BufferedInputStream>();
        
    }
}

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. 

 

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;

public class CaptureExampleLowerBoundedWildcard<E extends InputStream> {
	public void readFromIt(E readFrom) throws IOException {
		readFrom.read();
	}
	
	public E getSomething(){
        return null;
    }

	public static void main(String[] args) throws Exception {
		CaptureExampleLowerBoundedWildcard<? super FilterInputStream> ce = new CaptureExampleLowerBoundedWildcard<InputStream>();

		BufferedInputStream obj = new BufferedInputStream(new FileInputStream("somefile"));

		// Note that subclasses of the lower bound are allowed in method
		// invocation due normal method invocation conversion
		ce.readFromIt(obj); // ok
		
		InputStream b = ce.getSomething(); // ok

	}
}

 

通过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的子类,举例如下:
 
interface IA {}

class B implements IA {}

class Test6<T extends IA> {
	public void test() {		
		Test6<? super B> x = new Test6<B>(); // B不实现IA会报错
	}
}

 

(4)Otherwise, Si = Ti

 

public class CaptureExample<E, F extends E> {
    public void copy(List<E> toGet, List<F> copyFrom){
        for(F x:copyFrom){
            toGet.add(x);
        }
    }
    
    public static void main(String [] args){
        List<Object> aList=new ArrayList<Object>();
        List<String> copyFrom=new ArrayList<String>();
        copyFrom.add("Hi");
        CaptureExample<Object, String> ce=new CaptureExample<Object, String>();//Here is where the capture conversion takes place
        ce.copy(aList, copyFrom);
        
        System.out.println(aList);
        
    }
}

 

重新解读通配符:

e.g 1

 

public class CaptureExampleUnboundedWildcard<E extends InputStream> {

    // 方法形式参数表示设置值
    public void set(E readFrom) throws IOException{
        readFrom.read();
    }

    // 返回值为类型参数,表示取值
    public E get(){
        return null;
    }
    
    public static void main(String [] args) throws Exception{
    	
        CaptureExampleUnboundedWildcard<?> ce = new CaptureExampleUnboundedWildcard<InputStream>();
        
        
        
        //The method set(capture#1-of ?) in the type CaptureExampleUnboundedWildcard<capture#1-of ?> 
        //is not applicable for the arguments (BufferedInputStream)
        BufferedInputStream bis = null;
        ce.set(bis);
        
        //The method set(capture#2-of ?) in the type CaptureExampleUnboundedWildcard<capture#2-of ?> 
        //is not applicable for the arguments (InputStream)
        InputStream is = null;
        ce.set(is);
        
        ce.set(null);
        
        Object o1 = ce.get();
        InputStream o2 = ce.get();
        // Type mismatch: cannot convert from capture#6-of ? to FilterInputStream
        FilterInputStream o3 = ce.get();
    }
}

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

 

e.g 2

interface IA{}
class A{}
class B{}

public class CaptureExampleUpperBoundedWildcard<E extends A> {

	 // 方法形式参数表示设置值
    public void set(E readFrom) throws IOException{
    	
    }

    // 返回值为类型参数,表示取值
    public E get(){
        return null;
    }
    
    
    public static void main(String [] args) throws Exception{
        
    	//Bound mismatch: The type ? extends B is not a valid substitute for the bounded parameter <E extends A> 
    	//of the type CaptureExampleUpperBoundedWildcard<E>
        //CaptureExampleUpperBoundedWildcard<? extends IA> ce = new CaptureExampleUpperBoundedWildcard();
        
        CaptureExampleUpperBoundedWildcard<? extends IA> ce = new CaptureExampleUpperBoundedWildcard();

        // 当为<? extends Serializable>时不能存入任何元素,除了null值
        ce.set(null);  // 在方法的参数中就相当于给设置值,而在方法的返回中就相当于获取值
        
        Object o1 = ce.get();
        A o2 = ce.get();
        IA o3 = ce.get();
    }

}

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

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

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

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

 

 

e.g 3

class SubBufferedInputStream extends BufferedInputStream{
    public SubBufferedInputStream(InputStream in) {
        super(in);
    }
}
public class CaptureExampleLowerBoundedWildcard<E extends FilterInputStream> {


    // 方法形式参数表示设置值
    public void set(E readFrom) throws IOException{
        readFrom.read();
    }

    // 返回值为类型参数,表示取值
    public E get(){
        return null;
    }
    
    public static void main(String [] args) throws Exception{

    	CaptureExampleLowerBoundedWildcard<? super BufferedInputStream> ce=new CaptureExampleLowerBoundedWildcard<FilterInputStream>();

        //Bound mismatch: The type ? super InputStream is not a valid substitute for
        //the bounded parameter <E extends FilterInputStream> of the type CaptureExampleLowerBoundedWildcard<E>
        // CaptureExampleLowerBoundedWildcard<? super InputStream> ce = new CaptureExampleLowerBoundedWildcard<FilterInputStream>();

        //Now we can call this without an error, because the capture converted type does have a lower bound.
        //Note that subclasses of the lower bound are allowed in method invocation due normal method invocation conversion
        BufferedInputStream obj= null;
        ce.set(obj);

        SubBufferedInputStream sis = null;
        ce.set(sis);

        //The method set(capture#3-of ? super BufferedInputStream) in the type 
        //CaptureExampleLowerBoundedWildcard<capture#3-of ? super BufferedInputStream> is
        //not applicable for the arguments (FilterInputStream)
        FilterInputStream fis = null;
        ce.set(fis); // error

        FilterInputStream o1 = ce.get();
        // Type mismatch: cannot convert from capture#5-of ? super BufferedInputStream to SubBufferedInputStream
        SubBufferedInputStream o2 = ce.get(); // error
        // Type mismatch: cannot convert from capture#6-of ? super BufferedInputStream to BufferedInputStream
        BufferedInputStream o3  =  ce.get(); // error

    }

}

从上面的实例可以看出,通配符为? 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

 

 

 

 

 

转载于:https://www.cnblogs.com/extjs4/p/6284620.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值