I was reading a great article by a friend of mine Checking an
Unchecked Cast in Java 5 or Later where he refers to some of the rules from Josh Bloch about making type safe conversions. It was interesting. Glen also provides a good code example of how he does a type safe conversion.
I thought I would provide a couple of examples using a List converter. I have heard that a number of folks do not like returning
<T>
from a method which has a collection associated with it. However like any tool, it is up to the person using it not to hurt themselves (i.e. a screwdriver is not a hammer).
In this example I have a couple of methods which do type safe conversion of a
List
of something. We need to know in advance what we expect that something to be.
This is just one example of how you can do a conversion on a collection. I would like to note that there are methods on the
Collections
class which returns a checked collection. This ensures that any object added to a collection is of particular type. As the API notes, this is for the addition of classes after the collection has been turned into a checked collection. It does not check the Collection for previously added objects. The code below checks all of the object types before doing the conversion, and fails if it encounters the wrong type.
Looks like we were both up half the night working on the same thing! I posted my version this morning as well because it's a little different.
ReplyDeleteImmutableList is an interesting concept. I think it should have been an interface, the same as the existing List interface, but without the methods: add(), addAll(), clear(), remove(), removeAll(), retainAll(), and set(). The existing List interface should be extended from ImmutableList in order to add those methods. Ditto Set and Map should be extended from ImmutableSet and ImmutableMap. If a function took an ImmutableList argument, the caller would know that it couldn't modify the list. No casting, no creating new lists, no UnsupportedOperationExceptions would be required. If the objects it contains are immutable, an ImmutableList parameter would be truly unmodifiable the way a primitive parameter is.
It would make writing threadsafe code much easier. Create your list as mutable, then cast or pass it as immutable once it's constructed and as long as you don't change it, you never have to worry about its thread safety again.
Interesting blog posts and comments!
ReplyDeleteImmutable{List,Set,Map}, like you mention, would work great until you do want to change it. Unfortunately, I only see two non-desirable ways to do that. One would be to copy the data structure's objects that you want to keep and add the new objects and/or remove the unwanted objects as needed. This would be slow. Another approach would be to cast back to the mutable version and modify it. This would be even worse because it invalidates anything holding onto the data structure. (Ack, my immutable data structure mutated!)
Many functional programming languages, and I think Clojure especially, has some interesting approaches to this. I'm hoping to discuss this at an upcoming GreenJUG meeting ;-)
I was discussing your method with one of our other developers, and notice that you may want to include a check to verify that none the elements in the list are null, to ensure that you don't through a NullPointerException at line 8.
ReplyDelete>> if (o != null && !clazz.isAssignableFrom(o.getClass())) { ...
Other options that we looked at was:
=> if (clazz.instanceof(o)) {
Appeared to work about as well.
But in all cases, it definely provided an excellent learning opportunity.
James..
The enhanced for loop protects the list values from being null since we are guaranteed to iterate over all objects in the list.
ReplyDeletepublic static void main(String[] args) {
List list = new ArrayList();
for (int i = 0; i < 10; i++) {
list.add("X=" + i);
}
System.out.println(list.size());
list.remove(0);
System.out.println(list.size());
list.remove(5);
System.out.println(list.size());
for (int i = 0; i < list.size(); i++) {
System.out.println("i: " + i
+ " value: " + list.get(i));
}
System.out.println("\n");
int i = 0;
for (String s : list) {
System.out.println("i: " + i
+ " value: " + s);
i++;
}
System.out.println(list.size());
}