First-class functions really are a nice thing. Joel Spolsky devoted a whole blog entry to them. Java, sadly, doesn’t have first-class functions. This lack can make itself annoyingly noticeable when you have a class with many properties, and you need to formulate constraints and rules for these properties.
Enter the Element
Consider the following simple example of a class Element
with a single integer property width
:
class Element { private int width; int getWidth() { return width; } }
If you had to check that the sum of the widths across a given collection of elements stayed within a given limit, you might do something like the following:
class CheckConstraints { static boolean isWidthSumWithinLimit(final Iterable<Element> elements, final int limit) { int sum = 0; for (final Element e : elements) { sum += e.getWidth(); } return sum <= limit; } }
More Constraints – the Python Way
Simple enough. But then, of course, your requirements change, and your element gets an additional property height
:
class Element { private int width; private int height; int getWidth() { return width; } int getHeight() { return height; } }
And, inevitably, you need to check the same constraint for that new field. Conceptually, all you need to do in isWidthSumWithinLimit
is to replace the one tiny little call to getWidth()
with getHeight()
. (And perhaps give the method a different name.) If you had first-class functions, extracting that call out of the method would be easy. In Python (which has first-class functions), our Element
could look like this:
class Element: def __init__(self, width, height): self.width = width self.height = height def getWidth(self): return self.width def getHeight(self): return self.height
And the constraints could be checked in this way:
# the general sum checking case def isSumWithinLimit(elements, getter, limit): return limit >= sum([getter(e) for e in elements]) # the special case for widths def isWidthSumWithinLimit(elements, limit): return isSumWithinLimit(elements, Element.getWidth, limit) # the special case for heights def isHeightSumWithinLimit(elements, limit): return isSumWithinLimit(elements, Element.getHeight, limit)
I.e., we pass the Element
functions Element.getWidth
and Element.getHeight
as the getter
argument into the general isSumWithinLimit
, where they are called as getter(e)
. And everything is well.
The Java Way: Anonymous Inner Classes
In principle, you can pass around functions in Java. You just have to take the detour of wrapping your function in an interface and passing instances of said interface around via anonymous inner classes. Following that route, the Java equivalent of the above Python code could look like the following:
First, we define the interface which, given an Element
, lets us pick the value we are interested in:
interface IntGetter { Integer get(final Element e); }
Then we define a general version of our sum constraint check using that interface:
static boolean isSumWithinLimit(final Iterable<Element> elements, final IntGetter getter, final int limit) { int sum = 0; for (final Element e : elements) { sum += getter.get(e); } return sum <= limit; }
And finally, we define our two special cases for width and height using anonymous inner IntGetter
instances:
// ... the width static boolean isWidthSumWithinLimit(final Iterable<Element> elements, final int limit) { return isSumWithinLimit(elements, new IntGetter() { @Override public Integer get(final Element e) { return e.getWidth(); } }, limit); } // ... and the height static boolean isHeightSumWithinLimit(final Iterable<Element> elements, final int limit) { return isSumWithinLimit(elements, new IntGetter() { @Override public Integer get(final Element e) { return e.getHeight(); } }, limit); }
So the general isSumWithinLimit
method can handle whatever field we can make accessible via an IntGetter
. On the one hand, this helps the checking method concentrate on its core functionality of summing up numbers and comparing them to a limit. It no longer has to care about where those numbers come from. On the other hand, unfortunately, the code is horrendously long and verbose.
Extracting to Constants
It gets worse if you wanted to introduce more checks, such as the isEachWithinLimit
check, which compares every element individually to a given limit. As with the sum check, we define a general case based using an IntGetter
:
static boolean isEachWithinLimit(final Iterable<Element> elements, final IntGetter getter, final int limit) { for (final Element e : elements) { if (getter.get(e) > limit) return false; } return true; }
And if we want to use that check for widths, we write a specialized check using an anonymous inner class:
// the specific case for the width static boolean isEachWidthWithinLimit(final Iterable<Element> elements, final int limit) { return isEachWithinLimit(elements, new IntGetter() { @Override public Integer get(final Element e) { return e.getWidth(); } }, limit); } // ...
We find ourselves duplicating the anonymous width
-specific IntGetter
class. Consequently, for n properties and m constraints, our code’s verbosity could well be in O(n*m). As a simple remedy against both the bulkiness of the specialized check methods and the duplication of each specific getter class, we can extract the getter classes into static constants on the Element
:
class Element { private int width; private int height; int getWidth() { return width; } int getHeight() { return height; } static IntGetter WIDTH = new IntGetter() { @Override public Integer get(final Element e) { return e.getWidth(); } }; static IntGetter HEIGHT = new IntGetter() { @Override public Integer get(final Element e) { return e.getHeight(); } }; }
With these in place, we can formulate our various constraint checks a little less verbosely. The two general constraint checks don’t change, but the special cases do:
// sum of widths static boolean isWidthSumWithinLimit(final Iterable<Element> elements, final int limit) { return isSumWithinLimit(elements, Element.WIDTH, limit); } // each width static boolean isEachWidthWithinLimit(final Iterable<Element> elements, final int limit) { return isEachWithinLimit(elements, Element.WIDTH, limit); } // sum of heights static boolean isHeightSumWithinLimit(final Iterable<Element> elements, final int limit) { return isSumWithinLimit(elements, Element.HEIGHT, limit); } // each height static boolean isEachHeightWithinLimit(final Iterable<Element> elements, final int limit) { return isEachWithinLimit(elements, Element.HEIGHT, limit); }
Finally, each case has been reduced to the one-liner it really should be – quite similar to the original Python example code.
When there’s more than just Elements
So far, the static anonymous inner class constants have given us something like first-class fields on Element
. We can generalize the code to work with classes other than Element
by making the IntGetter
and the constraints generic with respect to the class on which they are called:
interface IntGetter<E> { Integer get(final E e); }
The constants in Element
don’t have to change much. They just have the type parameter added:
class Element { // ... snip ... static IntGetter<Element> WIDTH = new IntGetter<Element>() { @Override public Integer get(final Element e) { return e.getWidth(); } }; static IntGetter<Element> HEIGHT = new IntGetter<Element>() { @Override public Integer get(final Element e) { return e.getHeight(); } }; }
And we have to generify the general checker methods to allow other types:
// the general cases static <E> boolean isSumWithinLimit(final Iterable<E> elements, final IntGetter<E> getter, final int limit) { int sum = 0; for (final E e : elements) { sum += getter.get(e); } return sum <= limit; } static <E> boolean isEachWithinLimit(final Iterable<E> elements, final IntGetter<E> getter, final int limit) { for (final E e : elements) { if (getter.get(e) > limit) return false; } return true; }
Now, if we get a new class Thing
with an integer property depth
, we wrap depth
in a static anonymous inner class constant:
class Thing { private int depth; static IntGetter<Thing> DEPTH = new IntGetter<Thing>() { @Override public Integer get(final Thing e) { return e.depth; } }; }
And without further changes, we can call all our constraint checks on things and their depths:
static boolean isEachDepthWithinLimit(final Iterable<Thing> things, final int limit) { return isEachWithinLimit(things, Thing.LENGTH, limit); }
So, for the price of defining the static anonymous inner class constants, we can pass them around as fields. If you have few of these fields, and you have a lot of repetitive handling to do with them, using static anonymous inner class constants can help you elegantly reuse significant portions of the handler code.
Hidden below is the whole source for this small example along with a simple main class:
/** * Demonstration of simple Anonymous Inner Class Constants * * NB: Code does not reflect best practices with regard to packaging, etc. * * @author Hans Bering */ package simple.ex3; import java.util.ArrayList; import java.util.List; interface IntGetter<E> { Integer get(final E e); } class Element { private final int width; private final int height; Element(final int width, final int height) { this.width = width; this.height = height; } int getWidth() { return width; } int getHeight() { return height; } static IntGetter<Element> WIDTH = new IntGetter<Element>() { @Override public Integer get(final Element e) { return e.getWidth(); } }; static IntGetter<Element> HEIGHT = new IntGetter<Element>() { @Override public Integer get(final Element e) { return e.getHeight(); } }; } class Thing { private final int length; Thing(final int length) { this.length = length; } static IntGetter<Thing> LENGTH = new IntGetter<Thing>() { @Override public Integer get(final Thing e) { return e.length; } }; } class ConstraintChecker { static <E> boolean isSumWithinLimit(final Iterable<E> elements, final IntGetter<E> getter, final int limit) { int sum = 0; for (final E e : elements) { sum += getter.get(e); } return sum <= limit; } static <E> boolean isEachWithinLimit(final Iterable<E> elements, final IntGetter<E> getter, final int limit) { for (final E e : elements) { if (getter.get(e) > limit) return false; } return true; } static boolean isWidthSumWithinLimit(final Iterable<Element> elements, final int limit) { return isSumWithinLimit(elements, Element.WIDTH, limit); } static boolean isEachWidthWithinLimit(final Iterable<Element> elements, final int limit) { return isEachWithinLimit(elements, Element.WIDTH, limit); } static boolean isHeightSumWithinLimit(final Iterable<Element> elements, final int limit) { return isSumWithinLimit(elements, Element.HEIGHT, limit); } static boolean isEachHeightWithinLimit(final Iterable<Element> elements, final int limit) { return isEachWithinLimit(elements, Element.HEIGHT, limit); } static boolean isEachLengthWithinLimit(final Iterable<Thing> things, final int limit) { return isEachWithinLimit(things, Thing.LENGTH, limit); } } public class Example { public static void main(final String... args) { final Element e1 = new Element(2, 5); final Element e2 = new Element(4, 3); final List<Element> elements = new ArrayList<>(); elements.add(e1); elements.add(e2); System.out.println(String.format("elements width sum check = %s", ConstraintChecker.isWidthSumWithinLimit(elements, 5))); System.out.println(String.format("elements each height check = %s", ConstraintChecker.isEachHeightWithinLimit(elements, 5))); final Thing t1 = new Thing(7); final Thing t2 = new Thing(1); final List<Thing> things = new ArrayList<>(); things.add(t1); things.add(t2); System.out.println(String.format("things sum length check = %s", ConstraintChecker.isSumWithinLimit(things, Thing.LENGTH, 5))); } }
Evidently, there are endless possibilities to using anonymous inner class constants. You can turn them into generic properties by giving them a generic type parameter on their return value; or make them modifiable by adding a set
method; probably add some generic serialization, default handling, etc. – Have fun!