有没有办法让下面的代码更通用?

Is there a way to make the following code more generic?

我将 gson 用于 serialize/deserialize 和 java 8 流。以下是代码片段,

private long sumofTime() {
    Line[] lines = gson.fromJson(jsonString, Line[].class);
    return Arrays.stream(lines).filter(x -> x.hasTime())
                .mapToInt(x -> x.getTime).sum();
}

第 class 行看起来像,

public class Line {
    String name;
    String stamp;
    Integer time;
    Integer xx;
    Integer yy;
    Integer zz;
    ...
    ...

    boolean hasTotalTime() {
        return totalTime != null;
    }
    ...
    getters...setters...}

Stream用于检查每个数组元素(即Line)的特定变量(上例中的eg.time)是否不为空,然后得到所有时间的总和。

问题:Line对象中有大约30个变量需要求和,那么如何使解决方案更通用,而不是为每个变量编写一个求和方法。请注意,有超过 1000 个 Line 对象要处理,这就是为什么我认为 Stream 会更好。

不是每个属性都有一个字段,您可以为属性定义一个枚举,然后有一个从枚举到 int 的映射:

public enum LineAttribute {
    XX,
    YY,
    ZZ,
    ...
}

private final EnumMap<LineAttribute, Integer> attributes;

public Line() {
    attributes = new EnumMap<>(LineAttribute.class);
    // init all attributes to 0
    for (LineAttribute attr : LineAttribute.values()) {
        attributes.put(attr, 0);
    }
}

然后您可以遍历每个属性,获取它的行值并将这些值相加。

private long sumOf(LineAttribute attr) {
    Line[] lines = gson.fromJson(jsonString, Line[].class);
    return Arrays.stream(lines)
            .filter(x -> x.has(attr))
            .mapToInt(x -> x.get(attr))
            .sum();
}

您尝试做的部分是动态 属性 提取,部分是部分应用函数,两者在 Java 中都不是特别简单。您最好以不同的方式定义 Line class。我会使用 Map 来存储属性,如下所示:

public class Line {
    Map<String, Integer> props;
    public Line() {
        // Initialize props
    }

    public boolean has(String prop) {
        return props.containsKey(prop);
    }

    public Integer get(String prop) {
        return props.get(prop);
    }

    public void set(String prop, Object value) {
        return props.put(prop, value);
    }
}

现在,当您要查找一堆事物的总和时,您可以调用

public int sumOf(Line[] lines, String prop) {
    return Arrays.stream(lines)
               .filter(l -> l.has(prop))
               .reduce(0, Integer::sum);
}

您可以将一个函数传递给 sum 方法,以从要求和的每一行中获取值:

public int sumLines(Function<Line, Integer> extractor){
    Line[] lines = ...
    return Arrays.stream(lines).map(extractor)
        .filter(Objects::nonNull).mapToInt(i -> i).sum();
}
....
int time = sumLines(Line::getTime);

假设如果属性不存在,函数将 return 为空,但如果不是这种情况,您也可以传入 Predicate 进行过滤。