A destructive update to a variable or field means assigning a new value to it even though it already has a value assigned. Let’s have a look at the anamnesis of a small piece of code which I found in the VCS history.
ValueType x = null;if (yType.isSetType()) { x = yType.getValue(); } else { x = yType.addNewValue(); } x.doSomething(); |
x is first null, then something else is assigned depending on some condition. The next programmer introduced this:
ValueType x = null; if (yType.isSetType()) { x = yType.getValue(); } else { if (unsetFlag) { yType.unsetValue(); x = yType.addNewValue(); } } x.doSomething(); |
Now it still compiles, but a NullPointerException will occur at runtime – sometimes. That’s bad. The principle “Fail early, fail hard” is best applied by failing at compile time with a compile time error. The unnecessary destructive update prevented that. Better start with:
final ValueType x;if (yType.isSetType()) { x = yType.getValue(); } else { x = yType.addNewValue(); } x.doSomething(); |
Then after
final ValueType x; if (yType.isSetType()) { x = yType.getValue(); } else { if (unsetFlag) { yType.unsetValue(); x = yType.addNewValue(); } } x.doSomething(); |
it doesn’t compile anymore (can you see why?). The compiler caught the problem before it was too late.
The programmer is forced to take action and will change to what he really needed instead:
final ValueType x; if (yType.isSetType()) { x = yType.getValue(); } else { if (unsetFlag) { yType.unsetValue(); } x = yType.addNewValue(); } x.doSomething(); |
By the way, Eclipse doesn’t even show a warning for the code that causes the NPE. Especially when working with bad code, the inspection features of Intellij Idea are mandatory. When working with professionals, an editor and a compiler will do.
Another example:
int x = 7; if (b) x = 8; if (c) x = 9; |
if without else is usually bad, and destructive updates are used in this example as well. It’s not an example from the real world like the one above that caused a concrete problem, but still should be rewritten to this:
final int x; if (b) x = 8; else if (c) x = 9; else x = 7; |
Destructive updates are one of the biggest problems in old software monoliths. Just by declaring everything final, one will automatically move towards a functional programming style and avoid many problems. “Everything” means everything: parameters, Exceptions in catch blocks, fields, local variables. I noticed something interesting: Programmers who avoid destructive updates themselves are much, much better at detecting problems related to destructive updates in other people’s code. Destructive updates are not something that bad programmers recognise as a phenomenon, they just do it without being aware of the consequences.
Now a dispute between communities that recognise this principle is whether it should be followed absolutely in languages like Java or not. There are many situations in which it would be clumsy and inconvenient to absolutely avoid destructive updates in Java due to language limitations. Some programmers compromise in that case, others don’t.
]]>A common problem is to convert a List of type X to a List of type Y, given a well-defined function from X to Y. For example, we have a List<Double> and want a List<Long> containing those numbers rounded. In Java 8, there is a quick solution for this:
final List<Double> l = Arrays.asList(1.1, 2.9, 3.3, 4.6, 5.5); final List<Long> solution1 = l.stream().map(Math::round).collect(Collectors.toList()); System.out.println(solution1); // [1, 3, 3, 5, 6] |
Short enough, but not lazy: We may not always need to access every member of the List, and thus we don’t need to allocate memory for the whole result and calculate it all at once. In those situations, I use this utility method:
/** * Maps a List of one type to a List of another type. * * @param f the function used to convert from type E to type F * @param list the List to be mapped * @param <E> the domain * @param <F> the codomain * @return a new List with the elements mapped * @throws NullPointerException iff any argument is <code>null</code> */ public static <E, F> List<F> map(final List<E> list, final Function<E, F> f) { Objects.requireNonNull(list); Objects.requireNonNull(f); return new AbstractList<F>() { @Override public F get(final int index) { return f.apply(list.get(index)); } @Override public int size() { return list.size(); } }; } |
Usage example:
final List<Double> l = Arrays.asList(1.1, 2.9, 3.3, 4.6, 5.5); final List<Long> solution2 = map(l, Math::round); System.out.println(solution2); // [1, 3, 3, 5, 6] |
As you can see, line 2 is much simpler now (once map is available as a global utility method). It allocates very little memory, which does not depend on the size of the source List, and doesn’t run Math::round
yet. However, when the same number is accessed more than once, it has to calculate it again. So it’s not always the best solution.
Hint: show
Solution: show
]]>Solution: show
]]>So I chased his knight around for a bit, until he decided to interrupt the chase with a tempo.
Solution: show
]]>Hint: show
Solution: show
]]>Recently someone asked me how to convert a List of type X to a List of type Y, given a well-defined function from X to Y. I’ve seen some horrible “solutions” for that in real world programs, and I hope that some people will benefit from my way of doing this:
/** * Defines a Function from E to F. * @param <E> the domain * @param <F> the codomain */ public interface Converter<E,F> { F convert(E f); } |
/** * Maps a List of one type to a List of another type. * @param c the function used to convert from type E to type F * @param list the List to be mapped * @param <E> the domain * @param <F> the codomain * @return a new List with the elements mapped * @throws NullPointerException iff any argument is <code>null</code> */ public static <E,F> List<F> map(final Converter<E,F> c, final List<E> list) { if (c == null || list == null) { // fail early; particularly important when using lazy evaluation throw new NullPointerException(); } return new AbstractList<F>() { @Override public F get(final int index) { return c.convert(list.get(index)); } @Override public int size() { return list.size(); } }; } |
This is some pretty generic code that can be used in many situations. For example in web applications it’s often necessary to map a certain entity to a String representation to display on the page.
Here is a usage example:
final List<JFrame> frames = Arrays.asList(new JFrame("hello"), new JFrame("world")); final List<String> titles = map( new Converter<JFrame, String>() { @Override public String convert(final JFrame f) { return f.getTitle(); } }, frames ); System.out.println(titles); // prints: [hello, world] final List<Integer> nums = Arrays.asList(1,2,3); final List<Integer> inc = map( new Converter<Integer, Integer>() { @Override public Integer convert(final Integer f) { return f + 1; } }, nums ); System.out.println(inc); // prints: [2, 3, 4] |
Not as sweet as it would look like in a functional programming language like Haskell or Scala, but as good as it gets in Java.
See also:
java.util.List
JTiger had a rich set of features and predefined assertions:
Tony Morris developed Assertion Extensions for JUnit (at that time known as JUnitX; not to be confused with other frameworks sharing the same name) a while ago. It contained advanced assertions to be used with plain old JUnit. When Java SE 5 was in beta, he started with the development of JTiger. Because of his contract as an employee of IBM, he needed permission by IBM to publish it as Open Source, even though he developed it in his free time. That process took several months. JTiger even crossed the desk of Erich Gamma, who commented on it in a private email to Tony Morris.
While JTiger was in this approval process, TestNG has been released. At that time it had less fancy assertions (see examples above), but the concept was just as good and additional assertions have been introduced later. Also there were IDE-Plugins for Intellij Idea and Eclipse.
After JTiger has been approved by IBM and released, it gained a fair amount of community acceptance. Independent comparisons with JUnit and TestNG were rather positive, such as Justin Lee’s Test Framework Comparison.
In December 2005 I officially took charge of the project and Tony Morris retired from it. I never changed much except for the documentation and website, though.
Early in 2010 the transfer of the domain ownership for jtiger.org to me failed. I don’t know what went wrong. I got the correct release code from Tony Morris and gave it to my registrar. A few days later my registrar informed me that either the current registrar or the owner (Tony Morris) did not approve the transfer. That was the end of the website.
In 2008, Tony Morris released Reductio. Similar to QuickCheck, it uses Automated Specification-based Testing. Reductio has become part of the Functional Java API.
And of course you can still use JTiger:
JTiger tar.gz
JTiger MD5 tar.gz
JTiger zip
JTiger MD5 zip