将 Java final 关键字添加到在循环内构建实例的工作方法

Adding Java final keyword to working method that builds instances inside a loop

采用以下 POJO:

public class Widget {
    private String fizz;
    private Long buzz;
    private List<Fidget> collaborators;

   // Constructor, getters & setters
}

public class Fidget {
    private String fizz;
    private String foo;

    // Constructor, getters & setters
}

以及以下(有效)方法:

public void compriseWidgets(List<Fidget> fidgetList) {
    List<Widget> widgets = new ArrayList<Widget>();
    Widget currentWidget = null;

    for (Fidget fidget : fidgetList) {
        if (currentWidget == null || 
                !currentWidget.getFizz().equals(fidget.getFizz())) {

            currentWidget = new Widget();
            widgets.add(currentWidget);
            currentWidget.setFizz(fidget.getFizz());
            currentWidget.setBuzz(fidget.getFoo().length());
        }

        currentWidget.getCollaborators().add(fidget);
    }

    return widgets;
}

这里我们想要 return 一个 List<Widget> 并仅填充该列表:

  1. 从输入列表中的第一个 Fidget 开始(因此 currentWidget == null);和
  2. 如果 FidgetcurrentWidget 具有相同的 fizz

此外,无论 fizzes 是否匹配,我们都想继续将 collaborators 附加到 currentWidget

我的问题

新的代码风格指南要求我们用 final 声明 ALL 变量...这意味着我需要将上面的代码重构为如下所示:

public void compriseWidgets(final List<Fidget> fidgetList) {
    final List<Widget> widgets = new ArrayList<Widget>();
    final Widget currentWidget = null;

    for (final Fidget fidget : fidgetList) {
        ...
    }

    return widgets;
}

因为它需要在 Widget 内部 循环中创建一个新的 Widget,但需要外部(循环外部)引用 Widget我们可以添加 collaborators,我完全不知道如何用 final 重写它。有任何想法吗?另外,请注意,这不是我能做到的 "push back",我只需要弄清楚并让它与新的编码标准一起工作。

Builder 设计模式是构建不可变对象的好方法。 资源:

我喜欢这个设计模式的这个版本是它如何为您提供验证规则的完美位置对象创建之前。

应用于此问题的示例:

public class Widget {
    private final String fizz;
    private final Long buzz;
    private final List<Fidget> collaborators;

    private Widget(Builder builder) {
        this.fizz = builder.fizz;
        this.buzz = builder.buzz;
        this.collaborators = builder.collaborators;
    }

    public static Builder builder() {
        return new Builder();
    }

    public static class Builder {
        private String fizz;
        private Long buzz;
        private List<Fidget> collaborators = new ArrayList<>();

        public Builder addFizz(String fizz) {
            this.fizz = fizz;
            return this;
        }

        public Builder addBuzz(Long buzz) {
            this.buzz = buzz;
            return this;
        }

        public Builder addCollaborators(List<Fidget> fidgets) {
            collaborators.addAll(fidgets);
            return this;
        }

        public Builder addCollaborator(Fidget fidget) {
            collaborators.add(fidget);
            return this;
        }

        private void validate() throws InvalidArgumentException{
            ArrayList<String> invalidArguments = new ArrayList<>();
            boolean failedValidation = false;
            if (collaborators.isEmpty()) {
                invalidArguments.add("collaborators");
                failedValidation = true;
            }
            if (this.fizz == null) {
                invalidArguments.add("fizz");
                failedValidation = true;
            }
            if (this.buzz == null) {
                invalidArguments.add("buzz");
                failedValidation = true;
            }
            if (failedValidation) {
                throw new InvalidArgumentException(invalidArguments.toArray(new String[0]));
            }
        }

        public Widget build() {
            validate();
            return new Widget(this);
        }
    }
}

然后像这样创建一个有效的 Widget 对象:

Widget widget = Widget.builder().addFizz("test").addBuzz(999).addCollaborators(fidgets).build();

您的 compriseWidget 方法存在我在问题评论中提到的问题,否则我也会提供一个示例。

为了扩展我的评论,您可以或多或少地机械地转换您的示例代码,如下所示:

public List<Widget> compriseWidgets(final List<Fidget> fidgetList) {
    final List<Widget> widgets = new ArrayList<Widget>();
    final Widget[] currentWidget = new Widget[] {null};

    for (final Fidget fidget : fidgetList) {
        if (currentWidget[0] == null || 
                !currentWidget[0].getFizz().equals(fidget.getFizz())) {

            currentWidget[0] = new Widget();
            widgets.add(currentWidget);
            currentWidget.setFizz(fidget.getFizz());
            currentWidget.setBuzz(fidget.getFoo().length());
        }

        currentWidget.getCollaborators().add(fidget);
    }

    return widgets;
}

许多变量可以final没有任何特别的影响,包括Fidgets和Widgets的列表,以及增强for循环中的循环变量。原始方法中唯一的其他变量是 currentWidget,实现对其进行了修改。这可以替换为长度为 1 的 (final) 数组,然后可以将其第零个元素用作原始变量的直接替换。

同理,一个更麻烦的要求是您不能使用赋值语句(不考虑变量声明中的初始化程序 "assignments")。这正在推动一种更实用的编程风格,我想这可能是您的新指南的目的。那么,您可以这样处理它:

public List<Widget> compriseWidgets(final List<Fidget> fidgetList) {
    final List<Widget> widgets = new ArrayList<Widget>();
    final ListIterator<Fidget> fidgets = fidgetList.listIterator();

    while (addWidget(widgets, fidgets)) { /* empty */ }

    return widgets;
}    

private boolean addWidget(final List<Widget> widgets, final ListIterator<Fidget> fidgets) {
    if (fidgets.hasNext()) {
        final Fidget firstFidget = fidgets.next();
        final Widget currentWidget = new Widget();

        widgets.add(currentWidget);
        currentWidget.setFizz(firstFidget.getFizz());
        currentWidget.setBuzz(firstFidget.getFoo().length());
        currentWidget.getCollaborators().add(firstFidget);

        while (fidgets.hasNext()) {
            final nextFidget = fidgets.next();

            if (currentWidget.getFizz().equals(nextFidget.getFizz())) {
                currentWidget.getCollaborators().add(nextFidget);
            } else {
                fidgets.previous();
                return true;
            }
        }
    }

    return false;
}

这几乎是相同的技巧,只是不太明显。可变状态隐藏在调用堆栈中(每次调用 addWidget() 代表原始方法的 currentWidget() 的变化)和容器对象中,这次是 ListIterator.

可以在函数式编程方向走得更远。通常,例如,您可以考虑基于流的方法,但我认为在这种特殊情况下这不会完全干净。然而,更通用的函数式编程没有适用于 Streams 的约束。