60

[JavaSpecialists 251] - Collections.checkedCollection()

 6 years ago
source link: https://www.javaspecialists.eu/archive/Issue251.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

251Collections.checkedCollection()

Author: Dr. Heinz M. KabutzDate: 2017-11-30Java Version: 8Category: 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


Comments

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)

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

Related Articles

  • 260 Java Is Still Free 2018-09-17

    Is the Java Ecosystem still safe and robust or should we move to a different language? Maybe Go or Python? In this newsletter we look at whether Java is still a solid choice. Full Article

  • 298 Finding Permitted Subclasses 2022-02-28

    Sealed classes show us which subclasses they permitted. Unfortunately there is no way to do this recursively. In this newsletter we show how to navigate the sealed class hierarchy and write a ClassSpliterator that can then create a Stream of classes. Full Article

  • 168 The Delegator 2009-01-15

    In this newsletter we show the reflection plumbing needed for writing a socket monitor that sniffs all the bytes being sent or received over all the Java sockets. The Delegator is used to invoke corresponding methods through some elegant guesswork. Full Article

Browse the Newsletter Archive

Note: Your search query will be forwarded to Google.


Categories:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK