讨论泛型
泛型告诉编译器接收什么类型,编译器自动插入装换,并在编译时告知是否插入类型错误的对象,这样
可以使得程序更加安全,也更加清晰。
如何最大限度的享有这些优势,又能使整个过程尽可能的简单化
第二十六条 请不要使用原生态类型(有泛型就使用泛型)
1.原生态类型指的是类有泛型但是不使用泛型,比如List<E> ,原生态就是List,没有使用泛型,只是为了兼容
2.如果使用原生态类型,就失掉了泛型在安全性和描述性方面的优势
3.List不等于List<Object>,List<Object>却是原生态类型List的子类
4.List<?> 不能将任何元素(除了null之外)放到collection<?>中
5.必须在类文字中使用原生态类型,比如List.class、String[].class、int.class 是合法的,而List<String>.class、
List<?>.class不合法
6.instanceof 的时候使用原生态类型 o instanceof set
第二十七条,消除非收检警告(@SuppressWarning)
1.要尽可能地消除每一个首检警告,如果消除了所以的警告,就可以确保代码是类型安全的
2.如果无法消除警告,同时可以证明引起警告的代码是类型安全的,(只有在这种情况下)才
可以用一个@SuppressWarning("uncheck")注解来禁止这条警告
3.应该始终在尽可能小的范围使用@SuppressWarning注解
4.每当使用SuppressWarning("uncheck")注解时,都要添加一条注释,说明为什么这么做是安全的
第二十八条,列表优于数组 (如果不是追求很好的性能,推荐使用列表)
1.数组是协变的,如果Sub是Super的子类型,那么Sub[]就是Super的子类型,Object[] b = new Long[20] 可以的,
但是当b[0] = "dadda"的时候,运行时才会报错,所以数组是具体化的,他在运行时知道和强化他们的元素
类型
2.泛型是可变的对于任意两个不同的类型type1和type2,List<type1> 既不是List<type2> 子类,也不是它的父类
List<Object> b = new ArrayList<String>() 是不允许的,错误发生在编译期,所以,泛型是通过擦除来实现的,在编译时
强化他们的类型信息,并在运行时丢弃(或者擦除)
3.数组和泛型不能很好的混合使用创建泛型,参数化类型或者类型参数的数组都是非法的:new List<E>[],
new List<String>[],new E[],为什么呢?因为要它合法,编译器在其他正确的程序中发生的转换就会运行时失败
List<String>[] strings = new List<>[]; //假设它是可以的
Object[] objectArray= strings; //数组的协变,可以赋值
objectArray[0] = List.of(12,13,14); //Object数组可以接收任意类型的对象
String s = strings[0].get(0); //会发生类型转换
// strings[0].get(0) 会类型转换失败,因为Integer无法转成String
4.E ,List<E>,List<String> 是不可具体化的,不可具体化是指,运行时的信息比编译时的信息要少,唯一可具体化
的参数化类型是无限制通配类型,Map<?,?>,但是创建无限制通配类型数组是合法的,List<?>[] a = new List<>[] 是合法的
5.函数上的可变性参数,当使用不可具体化的类型就会有一条警告,可以使用@SafeVarargs去掉
6.如果 T[] a = (T[])array 出现警告,可以消除警告,又或者使用泛型列表替换数组,运行速度可能慢一点,但
运行时不会得到ClassCastException异常(如果不是追求很好的性能,推荐使用)
第二十九条,优先考虑泛型(使用泛型比需要在客户端使用类型转换的代码要更加的安全,
也更加容易,他们不需要转换)
1.chapter5/sample29/Stack
2.<E extends Type> 被称为有限制的类型参数 E必须是Type或者Type的子类
3.使用泛型比需要在客户端使用类型转换的代码要更加的安全,也更加容易,他们不需要转换
第三十条,优先考虑泛型方法
1.声明类型参数的类型参数列表,处在方法的修饰符及其返回值之间
2.泛型单例工厂:可以给所有必要的类型参数使用单个对象,但需要编写一个静态工厂方法,让他重复地给
每个必须的类型参数分发对象,例子 chapter5/sample30/GenericFactory
3.递归类型参数:例子 :<E extends Comparable<E>> ,解读就是这个E元素是Comparable或者是Comparable的一个子类
第三十一条,利用有效制通配符来提升Api的灵活性(<? extends E> <? super E>)
1.参数化类型是不可变的,Iterable<Number> 是无法接收Iterable<Integer>的,尽管Integer是Number的子类
2.有限制的通配符类型,E的某个子类型 <? extends E> <? super E> E的某个父类类型
3.为了获得最大限度的灵活性,要在表示生产者(<? extends E> )或者消费者(<? super E>)的输入参数上
使用通配符类型,product-extends;consumer-super
4.不要用通配符类型作为返回类型,如果类的用户必须考虑通配符的类型,类的API或许就会出错
5.使用时始终应该是comparable<? super T> 优先于comparable<T>,Comparator<? super T>优先于Comparator<T>
//product-extends;consumer-super
<E extends Comparator<? super E>> //E必须实现Comparator,而Comparator是在消费E,并产生一个比较值
6.如果类型参数只在方法中出现一次,就可以用通配符取代它
public static <E> void swap(List<E> list)
public static void swap(List<?> list) //这一种比较好,不需要关心类型参数
7.当使用List<?>作为参数,之后无法设置值时,可以配置一个辅助方法使用 List<E>作为参数
public static void swap(List<?> list){
swapHelper(list);//可以接收
}
public static void swapHelper(List<E> list){
list.set(1,E);
}
第三十二条,谨慎并用泛型和可变参数(可变参数和泛型不能良好的工作)
1.将值保存到泛型可变参数数组中是不安全的,但是带有泛型可变参数或者参数化类型的方法
在实践中用处很大,在jdk中存在Collections.addAll(Collection<? super T>c,T...elements)EnumSet.of(E first,E... rest),
这些方法的类型是安全的
//代码是没有错误的,但是运行时最后一段代码会ClassCastException
static void dangerous(List<String>... stringLists){ //List<String>... stringLists,这不就是泛型数组了吗?
List<Integer> intList = List.of(42);
Object[] objects = stringLists;
objects[0] = intList;
String s = stringLists[0].get(0)
}
2.@SafeVarargs注解是通过方法的设计者做出承诺,声明这是类型安全的
3.允许另一个方法访问一个泛型可变参数数组是不安全的
4.对于每一个带有泛型可变参数或者参数化类的方法,都要用@SafeVarargs进行注解
5.泛型可变参数方法在以下的条件是安全的(条件是都要成立)
1.它没有在可变参数数组保存任何值
2.他没有对不被信任的代码开放该数组(或者其克隆程序)
6.@SafeVarargs只能用在无法覆盖的方法上,java8在静态方法和final实例中才合法,java9在私有的实例方法上
7.可以使用list来替换可变参数数组
8.当一个参数化类型的变量指向一个不是该类型的对象时,会产生堆污染,它导致编译器的自动生成转换失败,
破坏了泛型系统的基本保证
第三十三,优先考虑类型安全的异构容器
1./chapter5/samp[e33/Favorites 是一个类型安全的异构容器,它的所有键都是不同类型的
public class Favorites {
//<?> 无限制通配符
private final Map<Class<?>,Object> favorites = new HashMap<>();
public <T> void putFavorite(Class<T> type,T instance){
//favorites.put(type,instance);
//防止键与值的类型不一致
favorites.put(type,type.cast(instance));
}
//(T) 可以使用Class<T>.cast(T)
public <T> T getFavorite(Class<T> type){
return type.cast(favorites.get(type)) ;
}
}
2.System.out.println("%n") ,%n返回特定平台的行分隔符
3.Class<T>.cast(T)动态的把参数T转换成class对象所表示的类型,失败抛出ClassCastException
4.Favorites类存在两大局限性
1.键的类型可以是Object.class,但是值的类型,可以是任意类型,键值的类型不一致,可以使用
favorites.put(type,type.cast(instance)); 保持一致,还可以追溯是谁把错误的类型放进集合,抛出ClassCastException
2.它不能用于不可具体化的类型中,也即是保存List<String>等,因为List<String>.class(其实无法这么写)和
List<Integer>.class(其实无法这么写)都是一个引用List.class
3.注解Api广泛使用有限制的类型令牌<T extends Annotation>;Class<T>.asSubclass(Annotation.class),它将调用它的class对象
转换成用其参数表示的类的一个子类,转换成功,返回参数,否则抛出ClassCastException
网友评论