






















Java是强类型语言 方法里的参数和返回值类型需要预先声明 但很多时候一段代码可以作用于多种数据类型 那么需要复制粘贴很多次这段代码 但这些代码只有参数类型或返回值类型不同而已 这时候就可以使用泛型来解决。
泛型的字面意思是广泛的类型 它其实是类型参数化。先用类型参数占位 然后到用时才将实际类型作为参数传进来 之后泛型占位的地方替换成该实际类型来运行 。其实现是通过类型擦除来实现的 编译器将泛型替换为Object 然后对象进来的时候检查对象类型跟实际类型是否相同 如果不同那么报错 如果相同 那么用Object接收 而对象出去的时候从Object强制转换成对应的实际类型。所以泛型只是比完全用Object 编译器多做了类型检查和强制类型转换的工作而已 这样更安全也更易读。
但是完全当做Object处理的话 还是很不方便。而如果限制了参数类型的上界的话 比如声明 T extends Number 那么传进来的类型一定是Number的子类 所以可以将其当做Number来处理 到类型擦除时不是用Object来代替泛型 而是Number。
泛型类T<Number> 不是泛型类T<Number子类Integer>的父类 否则前者的对象a可以指向后者的对象b 之后往a里添加Number的其他子类比如Double 但a其实指向b对象 那么相当于往Integer里添加Double 到时取出时编译器添加的强制类型转换语句将Double强制转成Integer会报错。但如果想把泛型类里的元素添加到泛型类T<Number> 可以声明参数类型为T extends Number 那么就会把Number的子类放入T<Number> 然后之后都当做Number来使用。
<T extends E>是定义类型参数 它声明了一个类型参数T 可以放在泛型类定义中类名后面、泛型方法返回值前面。而 <? extends E> 是实例化类型参数 它用于实例化泛型变量中的类型参数 只是这个具体类型是未知的 只知道它是E或者E的某个子类型。但它们经常可以达成相同目标。
因为通配符只声明了是E的某个子类型 无法确定具体的子类型 所以它只能读不能写入 否则写入后取出时涉及到强制类型转换 会报错。必须时借助带类型参数的泛型方法来完成。
有依赖关系的只能用类型参数表示 如拷贝对象 那么to必须得是from的父类 方法定义为 <from extends to,to> copy(from ,to) 但可以用通配符简化 如<to> copy(? extends to ,to) 。
泛型方法和通配符的关系总结如下:
<? extends E> 跟 <? spuer E> 意思恰好相反 <? extends E>限制进来的对象的类型是E的子类 因此可以将进来的对象用E接收 而<? spuer E>限制进来的对象的类型是E的父类 所以可以将E交给传进来的对象。
实现Comparable接口是一个很常见的业务 但假如Base类实现了Comparable接口 而Child继承自Base类 那么对于要求参数为<T extends Comparable<T>>的方法它不满足条件 因为Child继承自Base之后是Comparable<Base> 而不是Comparable<Child> 所以应该把参数改为 <T extends Comparable<? super T>> 这样只要求其父类实现了Comparable接口 然后Child才会满足条件。
可以把<>当做注释符号看待 因为编译之后泛型类声明的<T>会被类型擦除 所以每个泛型类只有一份class类型对象。
对于前面的Child类 如果它想自定义自己的比较方法 那么不能实现Comparable<Child>接口 只能覆盖Base的比较方法。而且方法A(Comparable<Base>) 和方法A(Comparable<Child>)是相同方法 不是参数不同的重构方法。
想通过类型参数创建对象只能通过反射。
对于泛型类声明的类型参数 也就是在类名后定义的 可以在实例变量和方法中使用 不能在静态变量和静态方法中使用 因为如果能的话 那么对于每种实例化类型都要有一份对应的静态变量和静态方法 而由于类型擦除 泛型类只有一份class类型对象 静态变量和静态方法是class类型对象的属性 且与类型参数无关 所以不能使用泛型类的类型参数。而如果静态方法是泛型方法 可以声明自己的类型参数 但这与泛型类的类型参数无关。
Java不支持创建泛型数组 如果要存放泛型对象 要么使用泛型类型的原始类型的数组 或者使用泛型容器 泛型容器内部使用Object数组 如果要转换泛型容器为对应类型的数组 需要使用反射。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。