# java中多返回值的优雅解决方案

一、通过返回数组(或集合)实现

 @Test
    public void test1(){
        int a = 10,b =7;
        int[] compute = compute(a,b);
        System.out.println("两数之和:"+compute[0]);
        System.out.println("两数之差:"+compute[1]);
    }
    private int[] compute(int a,int b){
        return new int[]{a+b,a-b};
    }
  • 显然这有致命的缺点,你无法通过下标直观的对应[0]是什么含义,[1]是什么含义,特别是别人阅读你的代码可能会有些许的压力。可维护性非常差。

二、通过参数列表带回返回值

  • 同样一种比较常用的方式,参考代码如下
    @Test
    public void test2(){
        int a = 10, b = 7;
        IntBox aBox = new IntBox(a);
        IntBox bBox = new IntBox(b);
        compute2(aBox,bBox);
        System.out.println("两数之和:" + aBox);
        System.out.println("两数之差:" + bBox);
    }

    private void compute2(IntBox a,IntBox b){
        int diff = a.value - b.value;
        int sum = a.value + b.value;
        a.value = sum;
        b.value = diff;

    }

    private class IntBox{
        private int value;

        public IntBox(int value){
            this.value = value;
        }

        @Override
        public String toString(){
            return String.valueOf(value);
        }
    }

缺点

  1. 基本类型不支持,必须封装成对象,比如上述代码封装了IntBox,造成代码量冗长。
  2. 反代码阅读直觉,可读性极差。在别人阅读你的代码时,看到方法调用,一般认为传递了xx参数,获取到xx返回值,而不是参数都甚至被改变,这极大的增加了维护和理解成本,他为了了解参数被发生什么改变,甚至必须深入方法内部逻辑。

>
>

此方法由于过于反直觉、代码量冗长,可读性、可维护性极差,是充满妥协的解决方案。但由于是最简单的方法,所以在新手中不少见,我个人建议不到万万不得已,别使用。

三、通过map实现

比较容易想到的另一种方案,多返回值通过key-value形式返回,示例如下:

   @Test
    public void test3(){
        int a = 10,b =7;
        Map<String, Integer> resMap = compute3(a,b);
        System.out.println("两数之和:" + resMap.get("sum"));
        System.out.println("两数之差:" + resMap.get("diff"));
    }

    private Map<String,Integer> compute3(int a,int b){
        HashMap<String, Integer> hashMap = new HashMap<>();
        hashMap.put("diff",a-b);
        hashMap.put("sum",a+b);
        return hashMap;
    }
  1. 此方式和数组返回值有相似的地方,可以当做前者的升级版,但map的key是可读的,而数组的下标是没有可读性的。
  2. 虽然消除了“数组返回值”时下标不可读性的理解困难问题,但引入的硬编码魔法值已经容易造成维护困难,比如后期可能忘记key的值是sum还是summation,甚至单词拼错都会把bug引入打运行时。这是动态语言常见的问题,在java身上不应该出现。

四、使用枚举作为key消除硬编码

hashMap作为返回值时,最大的缺点就是key的值是string魔法值,消除魔法值常见的办法就是枚举。因此考虑枚举替代String作为key,代码如下

    @Test
    public void test3(){
        int a = 10,b =7;
        Map<ResType, Integer> resMap = compute3(a,b);
        System.out.println("两数之和:" + resMap.get(ResType.SUM));
        System.out.println("两数之差:" + resMap.get(ResType.DIFF));
    }

    private Map<ResType,Integer> compute3(int a,int b){
        HashMap<ResType, Integer> hashMap = new HashMap<>();
        hashMap.put(ResType.DIFF,a-b);
        hashMap.put(ResType.SUM,a+b);
        return hashMap;
    }

    enum ResType{
        SUM,
        DIFF
    }

emmm,比上述好得多了,但是还是有优化空间。

  1. 在方法中必须显式创建一个hashMap比较麻烦。
  2. 第9行map的泛型是可以不指定的,如果遇到不规范的队友,依旧存在硬编码问题。
  3. 必须多定义一个枚举,一定程度上增加代码量

最终方案:基于enumMap的自定义返回值

创建自定义返回值MutiResult.java代码如下:

public class MutiResult<K extends Enum<K>,V>{
    private Map<K, V> resultMap;

    private MutiResult(Map<K, V> resultMap){
        this.resultMap = resultMap;
    }

    /**
     * 获取返回值
     *
     * @param key
     * @return
     */
    public V get(K key){
        return resultMap.get(key);
    }


    public static <K extends Enum<K>,V> Builder<K, V> build(){
        return new Builder<>();
    }

    /**
     * @param keyClass key的类型
     * @param valueClass value类型
     * @return 建造者对象
     */
    public static <K extends Enum<K>,V> Builder<K, V> build(Class<K> keyClass,Class<V> valueClass){
        return new Builder<>();
    }

    /**
     * 建造者
     *
     * @param <K>
     * @param <V>
     */
    public static final class Builder<K extends Enum<K>,V>{
        private Map<K, V> resultMap;

        Builder(Map<K, V> resultMap){
            this.resultMap = resultMap;
        }

        Builder(){
            this(new ConcurrentHashMap<>());
        }

        /**
         * 生成目标类
         *
         * @return
         */
        public MutiResult<K, V> build(){
            return new MutiResult<>(resultMap);
        }

        /**
         * @param key
         * @param value
         * @return
         * @throws IllegalArgumentException 多次添加同一个key抛出此异常
         */
        public Builder<K, V> add(K key,V value){
            if(resultMap.get(key) != null){
                throw new IllegalArgumentException("重复添加key:" + key);
            }
            resultMap.put(key,value);
            return this;
        }
    }

使用方法:

    enum ResType{
        SUM,
        DIFF
    }


    @Test
    public void test4(){
        int a = 10,b =7;
        MutiResult<ResType, Integer> resMap = compute4(a,b);
        System.out.println("两数之和:" + resMap.get(ResType.SUM));
        System.out.println("两数之差:" + resMap.get(ResType.DIFF));
    }

    private MutiResult<ResType, Integer> compute4(int a,int b){
        return MutiResult.build(ResType.class,Integer.class)
                .add(ResType.SUM,a+b)
                .add(ResType.DIFF,a-b)
                .build();
    }
posted @ 2019-07-14 10:58  树荫下的天空  阅读(5876)  评论(0编辑  收藏  举报