JDK-6409411 : (coll) checked collection of primitive type: can create but cannot add to it
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util:collections
  • Affected Version: 6
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • OS: generic
  • CPU: generic
  • Submitted: 2006-04-05
  • Updated: 2012-10-08
Related Reports
Relates :  
Relates :  
Description
A DESCRIPTION OF THE FIX :
  Bug Description : This is about java.util.Collection.checkedCollection(Collection c, Class type)
One can create checked collection of primitive type. But an instance of such collection is unusable because it's impossible to add any element to it. I propose to convert primitive type to its reference wrapper type while creating checked collection (or forbid creation of checked collection of primitive type to identify the problem early).
Subset of Releases affected : 1.5.0, Mustang b72.
Test case : See Test case section.
Diff baseline : Mustang b72
Diff (convertion to reference type) :
--- old\java\util\Collections.java	Thu Feb 16 00:12:30 2006
+++ new\java\util\Collections.java	Tue Feb 28 17:46:15 2006
@@ -2211,7 +2211,7 @@
             if (c==null || type == null)
                 throw new NullPointerException();
             this.c = c;
-            this.type = type;
+            this.type = toReferenceClass(type);
         }
 
         public int size()                   { return c.size(); }
@@ -2280,6 +2280,30 @@
             return zeroLengthElementArray;
         }
     }
+    // where
+        @SuppressWarnings("unchecked")
+        static <T> Class<T> toReferenceClass(Class<T> type) {
+            if(!type.isPrimitive())
+                return type;
+            if(type == boolean.class)
+                return (Class<T>) Boolean.class;
+            if(type == char.class)
+                return (Class<T>) Character.class;
+            if(type == byte.class)
+                return (Class<T>) Byte.class;
+            if(type == short.class)
+                return (Class<T>) Short.class;
+            if(type == int.class)
+                return (Class<T>) Integer.class;
+            if(type == long.class)
+                return (Class<T>) Long.class;
+            if(type == float.class)
+                return (Class<T>) Float.class;
+            if(type == double.class)
+                return (Class<T>) Double.class;
+            // void.class remains
+            throw new IllegalArgumentException(type + " is not permitted");
+        }
 
     /**
      * Returns a dynamically typesafe view of the specified set.
Alternative Diff (forbid creation of primitive type checked collection):
--- old\java\util\Collections.java	Thu Feb 16 00:12:30 2006
+++ new\java\util\Collections.java	Tue Feb 28 16:51:42 2006
@@ -2210,6 +2210,8 @@
         CheckedCollection(Collection<E> c, Class<E> type) {
             if (c==null || type == null)
                 throw new NullPointerException();
+            if (type.isPrimitive())
+                throw new IllegalArgumentException("primitive types are not permitted");
             this.c = c;
             this.type = type;
         }


JUnit TESTCASE :
// Test Case (convertion to reference type)
import java.util.*;

import junit.framework.TestCase;

public class CheckedCollectionTest extends TestCase {

	private Collection collection;

	public void setUp() {
		collection = Collections.checkedCollection(new ArrayList(), int.class);
	}

	public void testAdd() throws Exception {
		collection.add(10);
	}

	public void testAddAll() throws Exception {
		collection.addAll(Collections.emptyList());
	}

	public static void main(String[] args) {
		junit.textui.TestRunner.run(CheckedCollectionTest.class);
	}

}

// Alternative Test Case (forbid creation of primitive type checked collection):
import java.util.*;

import junit.framework.TestCase;

public class CheckedCollectionTest extends TestCase {

	public void test() throws Exception {
		try {
			Collections.checkedCollection(new ArrayList(), int.class);
			fail("primitive types should not be permitted");
		}
		catch(IllegalArgumentException exc) {
			// ok
		}
	}

	public static void main(String[] args) {
		junit.textui.TestRunner.run(CheckedCollectionTest.class);
	}

}

Comments
EVALUATION This issue stems from the fact that the static type of, say, int.class is Class<Integer>. That's useful in some cases, but leads to a slight impedance mismatch here. Treating int.class as if it were Integer.class is the DWIM solution. It makes some sense, but also encourages a bit of sloppiness. Also, anywhere that type tags are used in an API you're likely to have a similar situtation. In the absence of a more general policy of treating int.class (and friends) specially, it doesn't feel right to do so here. The first alternative solution is to catch the error up front and throw IAE. That should result in an exception being thrown sooner, but (in most cases) not much sooner. It's unlikely, but conceivable, that someone somewhere is using this as a silly idiom for creating an immutable empty collection. And again, there's the absence of a more general policy of treating int.class specially. All told, it's best to acknowledge that there are all sorts of ways of creating useless collections, and this is just another one. The second alternative solution is to add more detail to the message of the exception that's thrown when the client attempts to modify the collection. The message could explicitly call out the issue. There's no reason not to do at least this much.
27-07-2006

EVALUATION Contribution-Forum:https://jdk-collaboration.dev.java.net/servlets/ProjectForumMessageView?messageID=12490&forumID=1463
05-04-2006