如何使用可变参数重构代码 - 放入映射

Tom*_*Tom 0 java

我有四种不同数量参数的方法。如何重构这些方法(是否可以制作单个通用方法?)

public Map<String, Object> addToMap(String name, Object value){
        Map<String, Object> map = new HashMap<String, Object>();
        map.put(name, value);
        return map;
    }
    
    public Map<String, Object> addToMap(String name, Object value, String name2, Object value2){
        Map<String, Object> map = new HashMap<String, Object>();
        map.put(name, value);
        map.put(name2, value2);
        return map;
    }
    
    public Map<String, Object> addToMap(String name, Object value, String name2, Object value2, String name3, Object value3){
        Map<String, Object> map = new HashMap<String, Object>();
        map.put(name, value);
        map.put(name2, value2);
        map.put(name3, value3);
        return map;
    }
    
    public Map<String, Object> addToMap(String name, Object value, String name2, Object value2, String name3, Object value3, String name4, Object value4){
        Map<String, Object> map = new HashMap<String, Object>();
        map.put(name, value);
        map.put(name2, value2);
        map.put(name3, value3);
        map.put(name4, value4);
        return map;
    }
Run Code Online (Sandbox Code Playgroud)

这该怎么做?

rzw*_*oot 5

简单的回答

你这样做是错的。这个东西已经存在了;使用例如番石榴的ImmutableMap.builder,或使用现有的Map.of(). 这不是一样:您的方法栈“会有限制”,这是一个Map<String, Object>,但大概不值得重写ImmutableMap或Map.of API的只是利益。

复杂的答案

请注意,您的命名已关闭。addToMap不是一个好的方法名称,特别是对于创建全新地图的方法。addToMap只是尖叫“这将提供的参数添加到现有地图中”,而这些方法不会这样做。所以我冒昧地为你修正了你的名字。这些方法也没有状态,所以应该是静态的。

选项#1:传递到私有方法。

public static Map<String, Object> newMap(String name, Object value) {
    return newMap0(name, value);
}

public static Map<String, Object> newMap(String name1, Object value2, String name1, Object value2) {
    return newMap0(name1, value1, name2, value2);
}
    
public static Map<String, Object> newMap(String name1, Object value1, String name2, Object value2, String name3, Object value3) {
    return newMap0(name1, value1, name2, value2, name3, value3);
}
// and so on

private static Map<String, Object> newMap0(Object... v) {
    assert v.length % 2 == 0;
    var out = new HashMap<String, Object>();
    for (int i = 0; i < v.length; i +=2) out.put((String) v[i], v[i + 1]);
    return out;
}
Run Code Online (Sandbox Code Playgroud)

最后一种方法在类型上是一场灾难(自由地允许传入奇数个参数或非字符串键),这就是为什么它没有业务成为您的 API 的一部分。这就是为什么它是私人的。

选项2:建筑商

查看 ImmutableMap.builder 以获得完整的论文。这是一大堆复杂的代码,但它提供了一个相当不错的 API。您的目标是呼叫者可以这样做:

YourUtilityClassThing.newMap()
  .put("a", 5)
  .put("b", 10)
  .build();
Run Code Online (Sandbox Code Playgroud)

这具有“类型安全”的显着优势(您只能传入字符串键和任何对象作为值),以及允许无限数量的 k/v 对。缺点是,您的 API 需要大量代码才能完成这项工作。

如果你喜欢这种方法,我怎么强调你不应该重新发明这个轮子并使用例如番石榴的 ImmutableMap.Builder 代替。

选项 3:元组类型

制作一个显式元组类型并编写您的 API 文档,以表明它在不积极使用静态导入的情况下无法使用,但是对于静态导入,这很好。调用者最终会写:


import static com.foo.YourThingie.*;

....

newMap(p("a", 5), p("b", 5));
Run Code Online (Sandbox Code Playgroud)

您需要相当多的代码YourThingie才能将它们结合在一起,但它是类型安全的并且支持无限参数:

class YourThingie {
    public static final class KeyValuePair {
        String key; Object value;
    }

    public static KeyValuePair p(String key, Object value) {
        KeyValuePair kvp = new KeyValuePair();
        kvp.key = key; kvp.value = value;
        return kvp;
    }

    public static Map<String, Object> newMap(KeyValuePair... pairs) {
        var out = new HashMap<String, Object>();
        for (var pair : pairs) out.put(pair.key, pair.value);
        return out;
    }
}
Run Code Online (Sandbox Code Playgroud)

您可能想让 KeyValuePair 成为一个合适的类:不可变的,带有一个 2 参数的构造函数和一个合适的 toString/equals/hashCode impl。这要么涉及一整页的样板文件,要么使用 JDK16 的记录,或者使用 Lombok 项目@Value来获得它(免责声明:众所周知,我对lombok做出了很多贡献)。