Capture Conversion解读
(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.
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也满足了限定条件。
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
Capture Conversion解读的更多相关文章
- Thinking in Java——笔记(15)
Generics The term "generic" means "pertaining or appropriate to large groups of class ...
- java泛型小问题
几年前当Java5还未正式发布的时候,看到过一些人写的介绍Tiger中的新特性,当时对我第一感觉冲击最大的就是泛型(generics)和注释(annotation),因为它们直接影响了我们编码的语法习 ...
- javac后期需要重点阅读的类
(1)Annotate (300行) Enter annotations on symbols. Annotations accumulate in a queue,which is processe ...
- javac的泛型
?:在实例化对象的时候,不确定泛型参数的具体类型时,可以使用通配符进行对象定义. (1)?表示通配符,通配符 与 T 的区别 T:作用于模板上,用于将数据类型进行参数化,不能用于实例化对象. publ ...
- 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 ...
- Types方法之isSameType-isSuperType-isSubType
4.isSameType() 方法 /** * Is t the same type as s? */ public boolean isSameType(Type t, Type s) { retu ...
- Types方法之isCastable-isConvertible
5. Conversions and Promotions 5.1. Kinds of Conversion 5.1.1. Identity Conversion 5.1.2. Widening Pr ...
- Android MVP模式 谷歌官方代码解读
Google官方MVP Sample代码解读 关于Android程序的构架, 当前(2016.10)最流行的模式即为MVP模式, Google官方提供了Sample代码来展示这种模式的用法. Repo ...
- 2014百度之星资格赛 1001:Energy Conversion(水题,逻辑题)
Energy Conversion Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others ...
随机推荐
- Oracle EBS客户化程序中格式化金额
在Oracle EBS系统中,随处可见金额的显示格式,通常情况下都具有千分位符,同时有一定位数的精度,让我们先来看看一些现成的例子 上面这些列子中的金额都显示了千分位符,同时具备以2位小数,难道 ...
- [leetcode] 8. Maximum Depth of Binary Tree
可能是因为我是按难度顺序刷的原因,这个其实在之前的几道题里面已经写过了.题目如下: Given a binary tree, find its maximum depth. The maximum d ...
- Android-系统绘图真相
系统绘图真相:这篇博客是专门讲解,系统内部是如何控制图片的变化,例如:图片缩放/图片旋转/图片平移/等等 注意:⚠️在真实开发过程中:关于图片的 图片缩放/图片旋转/图片平移/等等 操作 是使用动画A ...
- 自我介绍+github注册
我是网络工程141的蒋易,学号是1413042018 个人兴趣:打篮球,打羽毛球,打游戏 github注册流程 id:Scorpio1 注册困难:1.刚开始网页没打开. 2.全英文的界面无法看懂,要查 ...
- 在Excel中导入文本文件(CSV/TXT),自定义隔离符号
经常需要在Excel中导入文本文件,但是需要自定义隔离符号,例如空格或者逗号,参考一下方法:
- 三.jenkins 在windows上配置master 和 agent(slave)
参考链接: https://wiki.jenkins-ci.org/display/JENKINS/Step+by+step+guide+to+set+up+master+and+slave+mach ...
- asp.net——Josn转DataTable(转)
使用UI框架开发的时候就常常用到DataTable转Json的情况,但是最近完成一个微信公众号开发的项目,需要把微信接口传过来的json值作为转为DataTable后绑定到服务器控件上. 在网上找了很 ...
- 跨域处理之Jsonp
一.认识Jsonp JSONP是一个非官方的协议,它允许在服务器端集成Script tags返回至客户端,通过javascript callback的形式实现跨域访问(这仅仅是JSONP简单的实现形式 ...
- 开发者常用的十款Chrome插件
本文是稀土掘金投稿,虽然其中有倔金的私货,是篇推广文,但我看过后认为内容确实不错,有些好插件还是第一次知道,对我很有帮助,考虑过后还是决定推荐给大家,最近我比较关注各种提高开发效率的工具与技巧,今后看 ...
- iOS 优秀开源项目
1. 界面 iCarousel: 用来做图片旋转显示效果(相册). MZTimerLabel:用来制作一个计时并显示时间的 label. MSDynamicsDrawerViewController, ...