为什么我们需要泛型
-
适用于多种数据类型执行相同的代码
-
泛型中的类型在使用时指定,不需要强制类型转换
泛型类
//泛型类
public class NormalGenric<T>{
private T data;
public NormalGeneric(T data){
this.data = data;
}
public T getData(){
return data;
}
public void setData(T data){
this.data = data;
}
public static void main(String[] args){
NormalGeneric<String> normalGeneric = new NormalGeneric<>();
normalGeneric.setData("ok");
System.out.println(normalGeneric.getData())
}
}
//泛型接口
public interface Genertor<T>{
public T next();
}
//泛型类 实现 泛型接口
public class ImplGenertor<T> implements Genertor<T>{
@Override
puclic T next(){
return null;
}
}
//泛型类 实现 泛型接口
public class ImplGenertor2 implements Genertor<String>{
@Override
puclic String next(){
return null;
}
}
//泛型方法 可以是一个完全独立的 泛型方法 不一定声明在泛型类 或者 泛型接口中
public class GenericMethod{
//泛型方法 <T> 这个是标志性
public <T> T genericMethod(T...a){
return a{a.length/2};
}
}
//泛型类 泛型接口 和 泛型方法区别
//泛型类是在new创建对象时候 声明这个泛型类的类型是什么
//泛型方法 在我们使用这个方法是告诉编译器是什么
//使用泛型 是应为在编译期就缺点传的参数类型
//通过泛型类使用泛型方法 不会影响泛型方法 只会影响泛型类中的普通方法
//A extends B 派生
限定类型变量
/**
*类型变量得限定
*/
public class ArrayAlg{
//求两个值得最小
public static <T> T min(T a, T b){
if(a.comapareTo(b) > 0) return a; else return b;
}
public static <T extends Compareable> T min(T a, T b){
if(a.compareTo(b) > 0){
return a;
}else{
return b;
}
}
//确定这个泛型实现了Comparable 可以多个实现的 如何类和接口混用 类只能有一个 必须是第一个 java单继承 多实现
public static <T,V extends Compareable&Serializable> T min(T a, T b){
if(a.compareTo(b) > 0){
return a;
}else{
return b;
}
}
static class Test();
public static void main(String[] args){
//TODO
}
}
public class ClassBorder<T extends Comparable>{
private T data;
public T min(T outter){
if(this.data.compareTo(outter) > 0){
return outter;
}else{
return this.data;
}
}
public T getData(){
return data;
}
public void setData(T data){
this.data = data;
}
public static void main(String[] args){
ClassBorder<String> classBorder = new ClassBorder<>();
classBorder.setData("mark");
System.out.pritln(classBorder.min("av"));
}
}
泛型中的约束和局限性
//原生类型
public class Restrict<T>{
private T data;
//不能实例化类型变量
//public Restrict(){
// this.data = new T();//这样不行
//}
//静态俞或者方法里不能引用类型变量
private static T instance; //不允许使用 因为使用泛型 创建对象时候才知道泛型类型 static 在虚拟机创建时先执行static 构造方法还没有执行 JVM虚拟机不知道这个泛型是谁、
//静态方法 本身是泛型方法就行
private static <T> T getInstance(){}
public static void main(String[] args){
Restrict<double> //不行 基本类型都是不行的 只能是包装类
//获取的是泛型的原生类型
Restrict<Double> restrict = new Restrict<>();//Double在JVM中是一个对象
//if(restrict instanceof Restrict<Double>)
//if(restrict instanceof Restrict<T>)
Restrict<String> restrictString = new Restrict<>();
System.out.println(restrict.getClass()==restrictString.getClass());
System.out.println(restrict.getClass().getName());
System.out.println(restrictString.getClass().getName());
//TODO
Restrict<Double>[] restrictArray;//可以定义数组
Restrict<Double>[] restricts = new Restrict<Double>[10]; //但是不能创建数组
}
}
public class ExceptionRestrict{
//泛型类不能 extends Exception / Throwable
//不能捕获泛型类对象
public <T extends Throwable> void doWork(T t){
try{
}catch(T t){//在泛型里不能捕捉泛型类对象的
}
}
public <T extends Throwable> void doWorkSuccess(T x) throws T{
try{
}catch(Throwable e){//在泛型里能抛泛型类对象的
throw x;
}
}
}
泛型类型的继承规则
public class Worker {
}
public class Employee extends Worker{
}
public class Pair<T>{
private static <T> void set(Pair<Employee> p){
}
public static void main(String[] args){
//Pair<Employee> 和 Pait<Worker> 没有任何继承关系
Pair<Employee> employeePair = new Pair<>();
Pait<Worker> workerPair = new Pair<>();
Employee employee = new Worker();
//Pair<Employee> employeePair2 = new Pair<Worker>();
Pair<Employee> pair = new ExtendPair();
set(employeePair2);//这样可以
set(workerPair);//这样不可以 出现了通配符的概念
}
//泛型类可以继承或者扩展其他泛型类 比如 List 和 ArrayList
private static class ExtendPair<T> extends Pair<T>{
}
}
通配符类型
public class Fruit{
private String color;
public String getColor(){
return color;
}
public void setColor(String color){
this.color = color;
}
}
public class Apple extends Fruit{
}
public class HongFuShi extends Apple{
}
fruit Apple HongFuShi
Orange
public class GenericType<T>{
private T data;
public T getData(){
return data;
}
public void setData(T data){
this.data = data;
}
}
public class WildChar<T>{
public static void print(GenericType<Fruit> p){
System.out.println(p.getData().getColor());
}
public static void use(){
GenericType<Fruit> a = new GenericType<>();
print(a);
GenericType<Orange> b = new GenericType<>();
print(b);//不行 类型继承 不代表 泛型继承
}
//传来的参数 只要继承Fruit就是符合条件的 方法上支持通配符 类上面不支持
public static void print2(GenericType<? extends Fruit> p){
System.out.println(p.getData().getColor());
}
public static void use2(){
GenericType<Fruit> a = new GenericType<>();
print2(a);
GenericType<Orange> b = new GenericType<>();
print2(b);
GenericType<? extends Fruit> c = b;
Apple apple = new Apple();
Fruit fruit = new Fruit();
c.setData(apple);
c.setData(fruit);
}
public static void printSuper(GenericType<? super Apple> p){
System.out.println(p.getData());
}
public static void useSuper(){
GenericType<Fruit> fruitGenericType = new GenericType<>();
GenericType<Apple> appleGenericType = new GenericType<>();
GenericType<HongFuShi> hongFuShiGenericType = new GenericType<>();
GenericType<Orange> orangeGenericType = new GenericType<>();
printSuper(fruitGenericType);
printSuper(appleGenericType);
printSuper(hongFuShiGenericType);//不行
printSuper(orangeGenericType);//不行 不是apple的超类
//表示GenericType的类型参数的下界是Apple
GenericType<? super Apple> x = new GenericType();
x.setData(new Apple());
x.setData(new HongFuShi());
x.setData(new Fruit());
Object data = x.getData();
}
}
泛型擦除
Java语言中的泛型,它只是在程序源码中存在,在编译后的字节码文件中,就已经替换为原生类型(Raw Type),并且在相应的地方插入了强制类型转型代码。因此,对于运行期的Java语言,ArrayList<int>和ArrayList<String>就是同一个类,所以泛型技术实际上是Java语言的一个语法糖,Java语言中的泛型实现方法称为类型擦除,基于这种方法实现的泛型称为伪泛型。 将一段Java代码编译成Class文件,然后再用字节码反编译工具进行反编译后,会发现泛型不见了,程序又变回了Java泛型出现之前的写法,泛型类型都变回了原生类型。
/**
*/
public class GenericClean {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("Author", "IT Sandwich");
System.out.println(map.get("Author"));
}
}
再看反编译后的class
System.out.println((String)map.get("Author"));
上面的泛型类型变成了原生类型
泛型使用注意事项
上面这段代码是不能被编译的,因为参数List<String>和List<Integer>编译之后都被擦除了,变成了一样的原生类型List<E>,擦除动作导致这两种方法的特征签名变得一模一样(在IDEA中是不行的,但是jdk的编译器却可以,因为jdk是根据方法返回值+方法名+参数来区分方法是否重复的)
弱记忆
JVM版本兼容问题:JDK1.5以前,为了确保泛型的兼容性,JVM除了擦除,其实还是保留了泛型信息(Signature是其中最重要的一项属性,它的作用就是存储一个方法在字节码层面的特征签名,这个属性中保存的参数类型并不是原生类型,而是包括了参数化类型的信息):弱记忆。 从Signature属性的出现我们还可以得出结论,擦除法所谓的擦除,仅仅是对方法的Code属性中的字节码进行擦除,实际上元数据中还是保留了泛型信息,这也是我们能通过反射手段取得参数化类型的根本依据。