泛型笔记
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的对象,省去了一次转换。
以上是关于泛型笔记的主要内容,如果未能解决你的问题,请参考以下文章