Skip to main content

Swing in a better world: Generics and arrays

Posted by alexfromsun on May 5, 2011 at 11:37 AM PDT

Generics doesn't work well with arrays. Everybody knows that you can't create a generified array,
but not many people really know what it was done this way.
A nice article from Brian Goetz helped me to understand the problem
when I studied the new features of JDK 5.

The arrays in java are covariant when generics are not. The following code clearly shows it:

String stringArray[] = new String[1];
        // compiles ok
        Object objectArray[] = stringArray;
        // throws ArrayStoreException
        objectArray[0] = new Object();

        ArrayList stringList = new ArrayList();
        // doesn't compile - incompatible types
        ArrayList objectList = stringList;

If a generfied program is compiled without a warning you can safely add an Object to your ArrayList of Objects.
However when there is an array of Objects passed to your method as a parameter, there is no way to understand an object of what type you can put there
(I am not sure if you can find it out via reflection).


So it is known that arrays and generics don't do well together. It means that it is not recommended to use two features of JDK 5 together,
I am talking about generics and varargs.

This reminds me one old bug found by Rémi Forax,
which was fixed by changing

protected void process(V... chunks) {
    }

to

protected void process(List chunks) {
    }

in the SwingWorker class, which did look awkward at the first glance.

An alternative implementation

I would be happy if I could create and safely use generified arrays in java, at the same time I can hardly remember if I ever saw a snippet of code that really benefited from the fact that arrays are covariant.
This feature of java arrays doesn't look attractive to me and I don't mind "deprecating" it, I don't feel it is correct that I can't create a generifed array when I never cast an array of one particular type to an array of its supertype.


Certainly all java code must be backward compatible with any new version of JDK,
I am thinking about a new warning produced by the compiler when covariant arrays are in use.
If there is no such a warning in your code it should be pretty acceptable to create a generified array, otherwise you are warned just like when you use a raw type of a generifed class. Something to discuss for the next JDKs?





This was an entry from the Swing in a better world series

Your comments are welcome!

Thanks

alexp

Related Topics >>

Comments

<p>&nbsp;to create a generic array you must ...

to create a generic array you must "reify" yourself the type with a Class<T> parameter to a genetic utility static method, that force a cast of the result of
<a href="http://download.oracle.com/javase/6/docs/api/java/lang/reflect/Array.html#newInstance(java.lang.Class, int)">java.lang.reflect.Array.newInstance()</a>
something like

public class ArrayUtils {

@SuppressWarnings(&quot;unchecked&quot;)
public static <t> T[] makeArray(Class<t> clazz, int length) {  return (T[]) java.lang.reflect.Array.newInstance(clazz, length); } @SuppressWarnings(&quot;unchecked&quot;) public static <t> T[] makeArray(Class<t> clazz, int... lengths) {    return (T[]) java.lang.reflect.Array.newInstance(clazz, lengths); }    }   </t></t></t></t>

usage:
@Test
public void testMakeSimple() {
String[] exp = new String[] { &quot;one&quot;, &quot;two&quot;, &quot;three&quot; };
String[] act = ArrayUtils.makeArray(String.class, exp.length);

                // explicit type &quot;String.class&quot;: exp.getClass().getComponentType() not works here ...

assertEquals(String.class, act.getClass().getComponentType());

for (int i = 0; i &lt; act.length; i++) {
act[i] = exp[i];
}

for (int i = 0; i &lt; act.length; i++) {
assertSame(exp[i], act[i]);
}
}

<p>Hi hut37,<br /> as Alex said, generics and reflection ...

Hi hut37,
as Alex said, generics and reflection doesn't play well together.
try:
  Object[] array = makeArray(int.class, 3);

Rémi

Hello <br/><br/> The problem is not how to find a trick ...

Hello




The problem is not how to find a trick and create a generified array, but the fact that it can't be used after that in a type-safe manner


Thanks

alexp

<p>Hi Alex,</p> <p>&gt; I can hardly remember if I ever saw ...

Hi Alex,

> I can hardly remember if I ever saw a snippet of code that really benefited from the fact that arrays are covariant

I don't agree. It's easy to find such snippet.Arrays.asList() is often used with an array of something which is not an Object. Also it's not that hard to find an example of array covariance in Swing.I remember trying to generify java.swing.tree and have some trouble with bad interactions between TreePath and DefaultTreeModel, so by example in DefaultTreeModel.nodeChanged, fireTreeNodeChanged is called with an array of TreeNode but takes an array of Object as parameter.

Anyway, you're fundamentally right that the bad interactions between generics and array is a part of the Java technical debt. I really hope that Java 9 will reified generics to reduce that debt to something moderate. Currently, this debt makes all attempts to move forward less easier than it should be. The actual discussions about lambda implementations is all about that.

Note that one way to reduce the debt is also to cut spending, what about deprecating wildcards ?

Rémi

 

<pr/> > It's easy to find such snippet.Arrays.asList() is ...

> It's easy to find such snippet.Arrays.asList() is often used with an array of something which is not an Object.

I didn't mean that an arrays which type is not an Object is bad,

I was talking about the situation when an array of a particular type is casted to an array of its supertype

> Also it's not that hard to find an example of array covariance in Swing.I remember trying to generify java.swing.tree and have some trouble with bad interactions between TreePath


> and DefaultTreeModel, so by example in DefaultTreeModel.nodeChanged, fireTreeNodeChanged is called with an array of TreeNode but takes an array of Object as parameter.

Yep, it is possible to find this kind of code in Swing, but it looks more like just an accident

that example with array of Objects instead of TreeNodes is just an API misdesign

>Note that one way to reduce the debt is also to cut spending, what about deprecating wildcards ?

I personally like wildcards and would like to focus on generics and arrays in this entry

Thanks much and have a great weekend!

alexp