美文网首页互联网科技程序员Java 杂谈
优雅编程之这样使用泛型,你就“正常”了(三十三)

优雅编程之这样使用泛型,你就“正常”了(三十三)

作者: 阿_毅 | 来源:发表于2016-10-24 22:53 被阅读270次

    开心一笑

    【女:“一天中最幸福的时刻就是下班后你骑自行车载着我到街角那边吃卤肉饭。”
    男:“说实话。”
    女:“你骑自行车载我去吃卤肉饭。”
    男:“说实话。”
    女:“卤肉饭……”】

    **提出问题******

    java开发中如何更好使用范型???

    解决问题

    励志图片

    以下来自《Effective Java》读书笔记:

    请不要在新代码中使用原生态类型

    参考高手文章:

    http://www.cnblogs.com/nayitian/archive/2013/08/08/3245496.html

    http://www.cnblogs.com/TwoWaterLee/p/5878056.html

    参数化类型,通配符类型和原生态类型对比:

    • Set<Object>是个参数化类型,表示可以包含任何对象的一个集合;
    • Set<?>则是一个通配符类型,表示只能包含某种未知对象类型的一个集合;
    • Set则是个原生态类型,它脱离了泛型系统。**前两种是安全的,最后一种不安全。

    无限制通配类型和原生态类型的区别是:通配符类型是安全的,原生态类型不安全。你可以将任何元素放入到原生态类型的集合中,但不能将除了null之外的其他任何元素放到Collection<?>中。

    例如:

    public static void main(String[] args) {
        List<String> strings = new ArrayList<>();
        unsafeAdd(strings, new Integer(42));
        String s = strings.get(0); // Compiler-generated cast
    }
    //重点在这里。list参数是原生态类型,整个编译过程都不会出现问题,但是运行时候就是会报ClassCastException异常
    private static void unsafeAdd(List list, Object o) {
        list.add(o);
    }
    

    两条例外:

    • a.在Class中只能使用原生态类型,因为泛型信息可以在运行时被擦除。
    • b.在操作instanceof时,使用参数化类型非法。
    消除非受检警告

    参考高手文章:
    http://blog.csdn.net/lqadam/article/details/52563155

    要尽可能地消除每一个非受检的警告。

    使用@SuppressWarnings("unchecked")注解,需要注意的是将该注解用在尽可能小的范围内,能在变量上使用的不在方法上使用,能在方法上使用的不在类上使用。

    使用@SuppressWarnings注解时,都要添加一条注释,说明为什么这么做是类型安全的

    列表优先于数组

    下面的代码片段是合法的,却是不符合预期:

    // 运行时候失败
    Object[] objectArray = new Long[1];
    objectArray[0] = “I don’t fit in”; // Throws ArrayStoreException
    

    但下面这段代码则不合法,但却提醒了你怎样才能达到预期:

    // Won’t compile!
    List<Object> o1 = new ArrayList<Long>(); // Incompatible types
    o1.add(“I don’t fit in”);
    

    明显,利用列表可以在编译时发现错误。我们当然希望在编译时发现错误了。

    总结:数组是协变的,数组是具体化的:在运行时才知道并检查它们的元素类型约束

    优先考虑泛型,优先考虑范型方法

    定义泛型方法语法格式如下:

    http://www.cnblogs.com/iyangyuan/archive/2013/04/09/3011274.html

    引用高手的图片

    参考高手文章:

    http://www.cnblogs.com/cutter-point/p/5883279.html

    修改前:

    //这里使用原生类型是不合理的
     public static Set union1(Set s1, Set s2){
        Set result = new HashSet(s1);
        result.addAll(s2);
        return result;
    }
    

    修改后:

     //这里的范型没有安全警告
     public static <E> Set<E> union(Set<E> s1, Set<E> s2){
        Set<E> result = new HashSet<E>(s1);
        result.addAll(s2);
        return result;
    }
    
    利用有限制通配符来提升API的灵活性

    参考高手文章:

    http://www.cnblogs.com/13jhzeng/p/5726511.html

    先看下面例子:

    修改之前:

    public void pushAll(Iterable<E> src) {  
        for (E e : src) {  
            push(e)  
        }  
    }  
    

    修改之后:

    //src是生成者,所以使用extend
    public void pushAll(Iterable<? extends E> src) {  
        for (E e : src) {  
            push(e)  
        }      
    }      
    

    pushAll的输入参数类型不应该为"E的Iterable接口",而应该为"E的某个子类型的Iterable接口",有一个通配符类型正符全此意:Iterable<? Extends E>

    PECS 表示:producer-extends, consumer-super

    如果参数化类型表示一个T生产者,就使用<? extends T>;如果它表示一个T消费者,就使用<? super T>。如果使用得当,通配符类型对于类的用户来说几乎是无形的。它们使方法能够接受它们应该接受的参数,并拒绝那些应该拒绝的参数。如果类的用户必须考虑通配符类型,类的API也许就会出错。

    假设添加一个popAll方法,从堆栈中弹出每个元素,添加到指定集合中:

    修改前

    public void popAll(Collection<E> dst) {
        while(!isEmpty) {
            dst.add(pop());
        }
    }
    

    与未修改的putAll一样,应当允许类型为Number的栈帧放在包括Number在内的父类型中。所以,修改为:

    修改后

    public void popAll(Collection<? super E> dst) {
        while(!isEmpty) {
            //这里dst是消费者,所以使用super
            dst.add(pop());
        }
    }
    

    总之,如果参数化类型表示一个T生产者,就使用<? extends T>,如果表示一个T的消费者,就使用<? super T>

    优先考虑类型安全的异构容器

    参考高手文章:

    http://blog.csdn.net/zhang_amao/article/details/52107612

    http://blog.csdn.net/tkd03072010/article/details/7722110

    public Class Favorites{
    
        public <T> void putFavorite(Class<T> type, T instance);
        public <T> T getFavorites(Class<T> type);
    }
    

    Favorites实例是类型安全的:当你向它请求String的时候,不会返回一个Integer给你。同时它也是异构的:不像普通的map,它的所有的键都是不同类型的。因此,我们将Favorites称作类型安全的异构容器

    Map不能保证键和值之间的类型关系,即不能保证每个值的类型都与键的类型相同,当你考虑到这个问题时,可以尝试使用类型安全的异构容器:

    public class Favorites {
        private Map<Class<?>, Object> favorites =
                new HashMap<Class<?>, Object>();
        
        public <T> void putFavorites(Class<T> type, T instance) {
            if(type == null)
                throw new NullPointerException();
            favorites.put(type, type.cast(instance));
        }
        
        public <T> T getFavorites(Class<T> type) {
            return type.cast(favorites.get(type));
        }
    }
    

    读书感悟

    来自三毛《稻草人手记》

    • 我们还年轻,长长的人生可以受一点风浪。
    • 我喜欢看见幸福的人,不管他们结不结婚。
    • 快回来吧!我希望把有生之年都静静地跟你分享。短短的人生我们不要再分开了啊。
    • 肉体的软弱是一时的 精神的胜利是永久的
    • 自由是多么可贵的事,心灵的自由更是我们要牢牢把握住的;不然,有了爱情仍是不够的。

    其他

    如果有带给你一丝丝小快乐,就让快乐继续传递下去,欢迎转载,点赞,顶,欢迎留下宝贵的意见,多谢支持!

    相关文章

      网友评论

        本文标题:优雅编程之这样使用泛型,你就“正常”了(三十三)

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