It is well known that Java programs (at least of the non-closured kind) tend to be rather verbose. Some people actually even prefer this, since it may make program more readable. In this post, however, I will try to show that verbosity isn't just a local issue. Rather, it affects code quality on a larger scale by increasing temptation to bad design - specifically, violating the law of demeter. Take the following simple Person class:
class Person {
String name;
int age;
}
Now assume we have a method containsName:
boolean containsName(List<Person>
for(Person p : list) {
if(name.equals(p.name)) {
return true;
}
}
return false;
}
Notice that the method is only interested in the names of the persons, so its dependency on class Person is merely accidental, unless we anticipate containsName to later utilize other details of class Person. In other words, methods like containsName tend to violate the law of demeter since they need to access the (sub(sub(sub)))fields of their arguments. This results in dreaded "train code", e.g., getInstance().getCompany().getPersons().first().getName().
Here's the version without the dependency:
Here's the version without the dependency:
boolean contains(List<String>
for(String x : xs) {
if(x.equals(y)) {
return true;
}
}
return false;
}
Such contains method is actually already included in List, so the above method is redundant. However, calling this method is a nightmare since we must "extract" the names of the Persons manually:
List<String>
for(Person p : persons) {
names.add(p.name);
}
contains(names, "Mikko");
To summarize: the hardness of extracting container fields results in temptation to use less verbose code that violates the law of demeter.
Luckily, with closures, the above will become clearer:
contains(persons.map({Person p => p.name}), "Mikko")
or, equivalently
persons.map({Person p => p.name}).contains("Mikko")
To preserve performance, introducing another method taking a key closure