java泛型详解

what:

  泛型则是将接口的概念进一步延伸,而泛型的意思就是广泛的类型,无论是类、接口还是方法都可以应用于非常广泛的类型。实现更灵活的代码复用,并且能提高代码的可读性和安全性。

  demo:

  泛型类:

public class Pair<T> {
    T first;
    T second;
    public Pair(T first, T second){
        this.first = first;
        this.second = second;
    }
    public T getFirst() {
        return first;
    }
    public T getSecond() {
        return second;
    }
}
Pair<Integer> minmax = new Pair<Integer>(1,100);
Integer min = minmax.getFirst();
Integer max = minmax.getSecond();

   泛型方法:

public static <U,V> Pair<U,V> createPair(U first, V second){
    Pair<U,V> pair = new Pair<>(first, second);
    return pair;
}
createPair("张三",1);
    与泛型类不同的是:使用的时候只需要传入确定类型的值即可,并不需要申明泛型类型后缀。

 

  基本原理

    对于Java的多态特性,我们完全可以定义一个通用的父类类型,然后传递具体的子类型,即直接使用Object。具体代码demo如下:

public class Pair {
    Object first;
    Object second;
    public Pair(Object first, Object second){
        this.first = first;
        this.second = second;
    }
    public Object getFirst() {
        return first;
    }
    public Object getSecond() {
        return second;
    }
}

Pair minmax = new Pair(1,100);
Integer min = (Integer)minmax.getFirst();//字段强制转换
Integer max = (Integer)minmax.getSecond();//字段强制转换

    上面的的使用方案也是可以的,其实在Java提供的泛型机制其实底层就是如此实现的。具体如下:      
      泛型设计的时候Java才到Jdk1.4版本,Java有编译器和Java虚拟机,编译器会帮我们把Java代码转换为.Class,虚拟机则是负责加载.Class。对于泛型类,
Java编译器会把泛型部分的代码转换为普通的代码,即和上面的Object类型接管一样,将类型的T进行擦除替换Object,并且进行必要的类型的强制转换操作。
Java虚拟机执行Java字节码的过程中,其实和Object操作是一样的,并不知道泛型,也不存在泛型。

  泛型存在的必要
    既然泛型会被擦除,为何还要在1.5中支持泛型??
    泛型具备两个优势:a、更好的安全性;b、更好的可读性。
    安全:对于上面的Pair类,直接使用Object,无论id是否为Integer类型,或者name是否为String类型,我们在编译阶段,由于类型为Object,我们都会进行强制转换操作
在编译期这些操作都是语法合理的,并不会报错。如果出错只有在运行阶段。但是如果我们使用的是泛型机制,并且使用的时候标明了类型为String和Integer,那么如果我们使用的类型不一致,在编译时已经报错
    可读:使用了泛型后,类的后缀添加对应的泛型类型,提高开发的可读性。

  
  泛型上限
    限制了泛型类型范围后,编译器检查的会更严格,泛型擦除的时候转换的类型则为指定范围上界的类型
    使用了extends关键字来表示的,当然这里的父类类型可以是接口、类或者类型参数。demo如下:
public static <T extends Comparable<T>> T max(T[] arr){ //  注意:Comparable接口本身也是个泛型接口
    T max = arr[0];
    for(int i=1; i<arr.length; i++){
            if(arr[i].compareTo(max)>0){
            max = arr[i];
        }
    }
    return max;
}
    此种方式可以实现泛型类型的递归类型限制传递。(Comparable)

  泛型通配符
    ?表示通配符,<? extends E>表示有限定通配符,具体需要匹配泛型E或者E的子类型即可,至于具体是什么类型,完全可以未知。demo如下:
public void addAll(DynamicArray<? extends E> c) {
    for(int i=0; i<c.size; i++){
        add(c.get(i));
    }
}
DynamicArray<Number> numbers = new DynamicArray<>();
DynamicArray<Integer> ints = new DynamicArray<>();
ints.add(100);
ints.add(34);
numbers.addAll(ints);
    numbers在这里E是Number类型的时候,?可以匹配为DynamicArray<Integer>,那么通配符和范围上界指定的效果一样,这两者有什么区别呢?
    a、<T extends E>写法仅限于用于定义类型参数;申明了一个类型参数T(使用的时候必须指定泛型类型)
    b、<? extends E>用于实例化类型参数;使用的时候可以不指定泛型类型,或者直接传递子类类型即可。
 
  无限定通配符
    无限定通配符,当然此通配符也可以使用泛型类型T来代替,效果是相同的,有一个限制--只能读,不可以写入。demo如下:
public static int indexOf(DynamicArray<?> arr, Object elm){
    for(int i=0; i<arr.size(); i++){
        if(arr.get(i).equals(elm)){
            return i;
        }
    }
    return -1;
}
DynamicArray<Integer> ints = new DynamicArray<>();
DynamicArray<? extends Number> numbers = ints;
Integer a = 200;
numbers.add(a); //代码错误,不允许添加
numbers.add((Number)a); //代码错误,不允许添加
numbers.add((Object)a); //代码错误,不允许添加

    无限定通配符泛型类型参数的关系,如下:

      1.无限定通配符能修饰的泛型,都可以使用泛型类型参数的方式替换
      2.通配符可以减少泛型类型参数,代码更简洁,可读性更好
      3.如果类型参数之间有依赖关系,或者返回值依赖于传递的类型参数,这里只能使用泛型类型参数

  泛型超类通配符:
    泛型在Java1.6中加入了超类型通配符操作,形式为<? super E>。demo如下:
public void copyTo(DynamicArray<? super E> dest){
    for(int i=0; i<size; i++){
        dest.add(get(i));
    }
}
DynamicArray<Integer> ints = new DynamicArray<Integer>();
ints.add(100);
ints.add(34);
//构建一个Number父类型的动态数组
DynamicArray<Number> numbers = new DynamicArray<Number>();
ints.copyTo(numbers);
 
 

 

 

 

 

 

 

 

 

     

posted @ 2022-04-24 12:02  修心而结网  阅读(171)  评论(0编辑  收藏  举报