泛型笔记

Posted LeaveMeAlone1995

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了泛型笔记相关的知识,希望对你有一定的参考价值。

泛型编程
一个储存键值的泛型类

public class Entry<K,V>{
    private K key;
    private V value;

    public Entry(K key,V value){
        this.key = key;
        this.value = value;
    }

    public K getKey() {
        return key;
    }

    public V getValue() {
        return value;
    }
}

值得注意的是:

Entry<String,int> a;//不能用基本类型实例化,int是基本类型,需要用Integer
Entry<String,Integer> entry = new Entry<>("www",123);

泛型方法

public class Arrays {
    public static <T> void swap(T[] array,int i,int j){//声明一个泛型方法时,类型参数要放在修饰符(public、static)之后。返回类型之前
        T temp = array[i];
        array[j] = array[i];
        array[i] = temp;
    }
}

Arrays.swap方法可以交换任何数组元素(只要数组中的元素类型不是基本类型)。
类型限定

public static <T extends AutoCloseable> void closeAll(ArrayList<T> elems)//如果有多个限定用&连接,比如T extend AutoCloseable & Runnable
    throws Exception{
    for(T elem:elems) elem.close();
}

extends AutoCloseable确保了elem.close是有效的,因为元素类型是AutoCloseable的子类型。

try {
    Arrays.closeAll( new ArrayList<String>());//不可行,因为String不是AutoCloseable子类
}catch (Exception e){
    e.printStackTrace();
}

类型变异和通配符
数组可以进行类型变异,比如Manager类是Employee的子类,可以将Manager[]数组传递给Employee[];
数组列表不可以进行类型变异,如果将ArrayList<Manager>赋给ArrayList<Employee>,就会存在下述情况。

ArrayList<Manager> bosses = new ArrayList<>();
ArrayList<Employee> empls = bosses;//非法,但假设其可行
empls.add(new Employee(...));//则一个普通员工会存在于bosses中
//如果一个方法从不对数组进行写操作,就不会破坏数组列表,可以用通配符表达这种情形
public static void printNames(ArrayList<? extends Employee> staff){//无论是什么类型,?都表示它是Employee的子类型,所以staff.get(i).getName()可以执行。
    for(int i=0;i<staff.size();i++){
        System.out.println(staff.get(i).getName());
    }
}

值得注意的是,不可以使用add方法,因为?可以表示任何子类。
父类型通配符

?super Employee代表Employee的一个父类型

public static void printAll(Employee[] staff, Predicate<? super Employee> filter){//
    for(Employee e:staff){
        if(filter.test(e)){
            System.out.println(e.getName());
        }
    }
}

Employee.printAll(new Employee[1],employee -> employee.toString().length()%2==0);
//Predicate接口是固定不变的,Predicate<Employee>和Predicate<Object>之间没有任何关系,所以采用了Employee的父类型<? super Employee>
PECS: produce(生产者) extends,consumer(消费者)super。从ArrayList中读取值,就是生产者,用extends.如果把Predicate用于测试,即为消费者,用super
带类型变量的通配符

Collections.sort()的定义:
public static <T extends Comparable<? super T>> void sort(List<T> list)//在这里用<? super T>的原因是防止Comparable的限制过于严格,在出现子类型继承父类型时,可能产生意外的影响。
只要T是Comparable的子类型,sort方法就可以对任意List<T>进行排序,而Comparable接口也是泛型的

public interface Comparable<T>{
    int compareTo(T other);
}
无限定通配符
public static boolean hasNull(ArrayList<?> objects){//元素类型无所谓,也可以采用泛型方法来实现
    for (Object e:objects){
        if(e == null){
            return true;
        }
    }
    return false;
}

 

java虚拟机中的泛型
java语言设计者决定在实现上将类型从虚拟机中“擦除”,促使用户使用泛型。
类型擦除,编译的时候泛型类型会被编译成原始类型,比如最上方的Entry类的泛型,会被编译城Object
转换插入,即编译成Object,但是返回值会被强制转换,例如String key = (String)entry.getKey();Object类型被强制转换为String类型

泛型约束
1.无基本类型参数 即不能产生一个ArrayList<int>
2.所有类型在运行时都是原始的,即不能在运行时查询一个ArrayList是否包含一个String对象。即if(a instanceof ArrayList<String>)//非法
同样转换成泛型类型也是无效的,但却是合法的

Object result = new ArrayList<String>();
    ArrayList<String> list = (ArrayList<String>)result;//
    @SuppressWarnings("unchecked") ArrayList<String> list = (ArrayList<String>)result;

滥用@SuppressWarnings会造成堆污染——本应该属于一个特定泛型类型实例对象,实际却属于一个不同的泛型,例如可以将一个ArrayList<Employee>赋值给ArrayList<String>引用。结果当检索到一个错误元素时,会抛出ClassCastException
3.不能实例化类型变量

    public static <T> T[] repeat(int n,T obj){
        T[] result = new T[n];//非法,不能用new T[...]构造一个数组
        for(int i=0;i<n;i++){
            result[i] = obj;
        }
        return result;
    }
    通过反射机制构建,参考了反射的那章的复制数组代码
    public static <T> T[] repeat(int n,T obj){
        Class<?> c = obj.getClass();
        Object newArray = Array.newInstance(c,n);
        for(int i=0;i<n;i++){
            Array.set(newArray,i,obj);
        }
        return (T[])newArray;
    }

值得注意的是,可以使用类型变量实例化ArrayList,即ArrayList<T> result = new ArrayList<>();//合法

4.不能构造参数化类型的数组

Entry<String ,Integer>[] entries = new Entry<String,Integer>[100];//非法,因为类型擦除后,数组构造函数将会创建一个原始的Entry数组,可以添加任意对象
    Entry<String ,Integer>[] entries2 = (Entry<String, Integer>[]) new Entry<?,?>[100];//合法,强制转换为对应类型

更简单是的构造数组列表。
5.静态上下文中的类类型变量不是有效的
即不能在静态变量或者静态方法中使用类型变量
6,类型擦除后的方法可能会冲突
类型擦除后的T被编译为Object有的方法会和Object自带的方法产生冲突
7.异常与泛型
不能够抛出或者捕获一个泛型类对象,甚不能构建一个Throwable的泛型子类
public class Problem<T> extends Exception//非法--泛型类不能是Throwable的子类
catch子句中可不能使用泛型变量

反射与泛型
Class<T>类
public Class<T>{
public T newInstance() throws...{...}
}
直接返回一个类型为T的对象,省去了一次转换。

以上是关于泛型笔记的主要内容,如果未能解决你的问题,请参考以下文章

笔记-java泛型详解

这个嵌套类构造函数片段可以应用于泛型类吗?

笔记:泛型

JAVA关于泛型的笔记

学习笔记Java基础知识——泛型与集合

C#高级编程笔记 Day 5, 9月 13日 (泛型)