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:
Fidget
in the input list (hence currentWidget == null
); andFidget
and currentWidget
have the same fizz
valueFurthermore, we want to keep appending collaborators
to the currentWidget
regardless of whether the fizzes match or not.
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.
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.