Search code examples
javafor-loopfinal

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


Take the following POJOs:

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
}

And the following (working) method:

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;
}

Here we want to return a List<Widget> and populate that list only:

  1. From the first Fidget in the input list (hence currentWidget == null); and
  2. If the Fidget and currentWidget have the same fizz value

Furthermore, we want to keep appending collaborators to the currentWidget regardless of whether the fizzes match or not.

My problem

A new code style guideline is requiring that we declare ALL variables with final...meaning I need to get the above code refactored to look like so:

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;
}

Because it requires both the creation of a new Widget inside the loop, but an external (outside the loop) reference to a Widget that we can add collaborators to, I'm at a total loss for how to rewrite this with final. Any ideas? Also, please note, this is nothing that I can "push back" on, I just need to figure it out and get it working with the new coding standard.


Solution

  • To expand on my comment, you could convert your example code more or less mechanically, like so:

    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;
    }
    

    Many variables can be made final without any particular impact, including the lists of Fidgets and Widgets, and the loop variable in the enhanced for loop. The only other variable in the original method was currentWidget, which the implementation modifies. This can be replaced with a (final) array of length 1, whose zeroth element can then be used as a drop-in replacement for the original variable.

    A more troublesome requirement along the same lines would be that you may not use assignment statements (initializers in variable declarations not being considered "assignments"). This is pushing toward a more functional style of programming, which I suppose may be the intent of your new guideline. You might, then, approach it something like this:

    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;
    }
    

    This is pretty much the same trick, just a little less obvious. The mutable state is hidden in the call stack (each invocation of addWidget() stands in for a mutation of the original method's currentWidget()) and in a container object, this time a ListIterator.

    One could go further in the functional programming direction. In general, for example, you could look toward Stream-based approaches, though I don't think that works out completely cleanly in this particular case. More general functional programming does not have the constraints that apply to Streams, however.