251 Collections.checkedCollection()

Author: Dr. Heinz M. Kabutz Date: 2017-11-30 Java Version: 8 Category: Language

Abstract: Since Java 5, we have been able to create collections that would check at runtime that the objects added were of the correct type. But very few Java programmers know about it. The benefit we get in debugging ease is huge, as exceptions are thrown earlier.

Welcome to the 251st edition of The Java(tm) Specialists' Newsletter. Exactly 17 years ago today, with fear and trepidation, I sent out the first edition of my newsletter to 80 friends and colleagues that I had gleaned from my inbox. I expected mocking and laughter down the corridors. There were some who made fun of my pretentious style and my title "Java Specialist", but the majority liked it and encouraged me to carry on. I had been tinkering with Java for three years already at the time and had made a few interesting discoveries. Over the years I supplemented the newsletter with Extreme Java corporate training and, of course, the hottest Java unconference in the world (Crete in July FTW). It has been too much fun!

Even more fun, I have been steadily honing my skills as a television personality. Living on Crete has some disadvantages. To get anywhere requires at least two flights. Often three. I travel so much that by March my Senator Status Star Alliance Gold Card is safe. Fortunately a new craze has hit this world, that of self-study training. And so I have launched Java Specialists Learning, where you can take all of my courses from the comfort of your sofa.

javaspecialists.teachable.com: Please visit our new self-study course catalog to see how you can upskill your Java knowledge.

Collections.checkedCollection()

Dinosaurs roamed the earth. Fred Flintstone wrote Applets with JBuilder. A shaft of lightning split the dark sky and with a flash <> appeared on his amber screen. Fred scratched his head. "What on flat earth was List<String>?"

It was the advent of generics.

Programmers do not like change. Or rather, we do not like change that will break something that was working perfectly well before. Java 1.4 introduced the assert keyword. It broke a bunch of our classes. Java 5 added enum to the relatively short list of reserved words, a popular name for Enumeration instances. And that was the last time a new keyword was added to the Java Programming Language. Sun Microsystems' engineers knew their audience. They also knew that for their generics to be accepted, old code should preferably still compile without any changes necessary.

Generics were designed so we could ignore them if we wanted to. The javac compiler would issue a faint sigh, but would still compile everything as before. How did they do it? Type erasure was the magic ingredient. When they compiled the class ArrayList<E>, the generic type parameter was erased and replaced with Object. Even the E[] was erased to Object[] . In Java 6, they changed the element array in ArrayList to the more honest Object[] .

By not distinguishing at runtime between ArrayList<String> and ArrayList<Integer>, we allowed Java programmers to still shoot themselves in the foot like so:

import java.util.*; public class FootShootJava5 { public static void main(String... args) { List<String> names = new ArrayList<>(); Collections.addAll(names, "John" , "Anton" , "Heinz" ); List huh = names; List<Integer> numbers = huh; numbers.add(42); } }

Sure, javac would emit a warning, but at runtime everything would appear to work. It was only when we retrieved elements from ArrayList<String> that a cast to String was inserted into the client code and then a ClassCastException would jump in our faces. This is an example of an exception that is thrown late. A while after the incorrect object has been inserted into the ArrayList<String> , we discover that it wasn't a String after all, thus if we add the following we see the problem:

import java.util.*; import static java.util.stream.Collectors.*; public class FootShootJava8 { public static void main(String... args) { List<String> names = new ArrayList<>(); Collections.addAll(names, "John" , "Anton" , "Heinz" ); List huh = names; List<Integer> numbers = huh; numbers.add(42); System.out.println(names.stream().collect(joining( "+" ))); } }

Results in a rather grumpy:

ClassCastException: Integer cannot be cast to CharSequence at ReduceOps$3ReducingSink.accept() at ArrayList$ArrayListSpliterator.forEachRemaining() at AbstractPipeline.copyInto() at AbstractPipeline.wrapAndCopyInto() at ReduceOps$ReduceOp.evaluateSequential() at AbstractPipeline.evaluate() at ReferencePipeline.collect() at FootShootJava8.main

Since the exception is thrown late, it results in wasted programmer effort searching for where the wrong type could have been inserted into the list.

And yet there has always been a better way, even in Java 5. We can wrap our List object with a checkedList. This way, every time we add an element, it is checked that it is of the correct type. The ClassCastException thus happens during the add(42) , rather than much later. For example:

import java.util.*; import static java.util.stream.Collectors.*; public class FootShootWithSafetyCatch { public static void main(String... args) { List<String> names = Collections.checkedList( new ArrayList<>(), String. class ); Collections.addAll(names, "John" , "Anton" , "Heinz" ); List huh = names; List<Integer> numbers = huh; numbers.add(42); System.out.println(names.stream().collect(joining( "+" ))); } }

We would still get a ClassCastException, but at the place where the damage was done:

ClassCastException: Attempt to insert class Integer element into collection with element type class String at java.util.Collections$CheckedCollection.typeCheck() at java.util.Collections$CheckedCollection.add() at FootShootWithSafetyCatch.main

The checked collection would also discover objects that are added via reflection and throw a ClassCastException. It could not safeguard against "deep reflection", but then not much can.

You might wonder why I am writing about a method that was added in Java 5? The reason is that hardly anybody I speak to has heard of Collections.checkedCollection() and its derivatives. It is useful to make your collections just a bit more robust against accidental or deliberate tomfoolery.

It can also be a quick and easy way to debug any ClassCastException you might discover in your system. Wrap the collection in a checked exception and the guilty party will quickly come to the fore.

Oh one last thing, completely unrelated to Java, but definitely related to our profession. Today also marks one year since I started my running streak, running at least one mile a day, in snow, rain, lightning and 48C heat. It's been fun and a great way to think about all sorts of things. I've had far more energy, have slept better and have produced more this year than in many previous years. If you're a couch potato, I can only recommend you try Streak Running and join me in the list of people who've run for at least one year, every day. No excuses.

Kind regards from Crete

Heinz

We are always happy to receive comments from our readers. Feel free to send me a comment via email or discuss the newsletter in our JavaSpecialists Slack Channel (Get an invite here)

Load Disqus comments

When you load these comments, you'll be connected to Disqus. Privacy Statement.

Please enable JavaScript to view the comments powered by Disqus.

Related Articles

021 Non-Virtual Methods in Java 2001-05-31 In Java all methods are virtual, meaning that the most derived method is always called. In earlier versions of Java, it was possible to trick the JVM by replacing the superclass with another wheere the method is defined as "private". Full Article

197 What is the Meaning of Life? 2011-12-06 In this newsletter we try to calculate the meaning of life, with surprising results. Full Article

223 ManagedBlocker 2014-11-27 Blocking methods should not be called from within parallel streams in Java 8, otherwise the shared threads in the common ForkJoinPool will become inactive. In this newsletter we look at a technique to maintain a certain liveliness in the pool, even when some threads are blocked. Full Article

Browse the Newsletter Archive