• JDK8漫谈——增强接口


    解决什么问题

    • 向下兼容。添加方法,所有的实现类必须实现此方法,否则会编译报错。这意味着每一次的接口升级都会伤筋动骨。但是这是一把双刃剑一定要把握好场景,不要滥用。
    • 类爆炸。使用时,需要辅助类。即要记忆接口可能还需要记忆辅助类。
    • 内置行为。使用时,需要关注外部的过程性的处理逻辑。比如:循环,排序,过滤,转换类型,取默认值等。

    背后思想

    • 封装。通过封装的思维,把细节封装成方法。通过服务的调用解决代码复用和关注点太高的问题。
    • 兼容性。很多软件一升级就得重新学一遍~~

    内容介绍

    组成部分

    接口一直不断在增强,但是总体来说,还是只放抽象的内容,不放实例化相关的内容。

    • 公有常量。
    • 公有方法。没有具体实现,一般不需要写public,默认的就是。
    • 私有方法(JDK9)
    • 静态方法。有具体实现,和静态方法调用一直。
    • 默认方法。有具体实现,可以被实现类重写并且直接调用。
    public interface Powerable {
        /**
         * 公有常量
         */
        String NAME = "me";
    
        /**
         * 公有方法
         */
        void print();
    
        /**
         * 静态方法
         * @param name
         */
        static void cry(String name){
            System.out.print(name+"is crying");
        }
    
        /**
         * 默认方法
         */
        default void fly(){
            System.out.print(Powerable.NAME+"can fly");
        }
    
    }
    

    注意事项

    • 不允许有成员变量。

    方法优先级

    • 类优先于接口。如果一个子类继承父类和接口有相同的方法实现。那么子类继承父类方法
    • 子类中的方法优先于父类中方法
    • 如果以上条件都不满足,则必须显示覆盖/实现其方法,或者声明成abstract

    实战

    最佳实践

    减少类爆炸

    经常使用到的抽象逻辑封装成默认方法,减少辅助类类。

    @Test
    public void test_default(){
        Map<String,String> map = new HashMap();
        String defaultIntro = "";
    
        //方案一,需要使用工具类
        String utilsIntro = MapUtils.getorDefault(map, "intro", defaultIntro);
        System.out.println(utilsIntro);
    
        //方案二,无需使用工具类
        System.out.print(map.getOrDefault("intro", defaultIntro));
    }
    
    • 减少需要思考多份职责,需要关注接口还需要关注类。
    • 减少类,提供代码优雅度。

    内置处理

    细节封装成默认方法,减少关注度和出错率,提升服务的稳定和优雅性

    public void test_default_encapsulation() {
        Map<String, String> map = new HashMap<>();
        //方案一:通过显式的循环,感知的细节太多。需要显示感知类型,还需要关注Entry对象以及getKey和getValue
        for (Map.Entry entry : map.entrySet()) {
            System.out.println(entry.getKey() + "," + entry.getValue());
        }
        //方案二:通过显式的循环,感知的细节太多。需要显示感知类型,还需要key以及map获取
        for (String key : map.keySet()) {
            System.out.println(key + "," + map.get(key));
        }
        //方案三:内置方法,不用考虑循环,不需要考取取值细节
        map.forEach((key, value) -> {
            System.out.println(key + "," + value);
        });
    }
    
    • 封装细节,把细节变成方法。
    • 改变思维方式,从面向细节到面向服务。原来是:循环map,通过什么类型获取key,value,执行任务,任务里使用key,value;现在是:map循环执行任务,任务里使用key,value。

    最佳反例

    放具体的实例

    比如把原来的工厂放到现在的接口默认方法里。

    default Powerable run(int type) {
        switch (type) {
            case 1:
                return new Person();
            default:
                return null;
        }
    
    }
    
    • 接口不稳定,会不断变修改。
    • 接口依赖实现,本末倒置。

    使用default来完成多态

    子类重写父类的default方法

    • 关注点太高,接口职责不清晰。需要看一下接口有哪里default,然后还需要判断哪些是需要重写的,哪些只能能用逻辑。

    再次思考

    • 接口是否变成万能类,职责越来越多,越来越重?
    • 什么时候使用抽象类,什么时候使用接口?
    • 什么时候使用工具类,什么时候使用接口的静态方法?
    • 如何减少重复思考,如何界定边界?

    参考资料

  • 相关阅读:
    Renderscript 浅析 (三)
    renderscript 浅析(二)
    renderscript 浅析(一)
    小米MACE开源框架搭建
    Word2vec
    最优化算法(牛顿、拟牛顿、梯度下降)
    聚类算法一(Kmeans、层次类聚、谱类聚)
    分类算法三(贝叶斯)
    js 创建方法
    XML与JSON的区别
  • 原文地址:https://www.cnblogs.com/ansn001/p/9375792.html
Copyright © 2020-2023  润新知