第五章

作者: gcno93 | 来源:发表于2022-01-01 00:30 被阅读0次

    讨论泛型
    泛型告诉编译器接收什么类型,编译器自动插入装换,并在编译时告知是否插入类型错误的对象,这样
    可以使得程序更加安全,也更加清晰。
    如何最大限度的享有这些优势,又能使整个过程尽可能的简单化

    第二十六条 请不要使用原生态类型(有泛型就使用泛型)
    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

    相关文章

      网友评论

          本文标题:第五章

          本文链接:https://www.haomeiwen.com/subject/bqfoqrtx.html