views:

6319

answers:

3

Just for review, can someone quickly explain what prevents this from working (on compile):

private HashSet Data;

...

public DataObject[] getDataObjects( )
{
    return (DataObject[]) Data.toArray();
}

...and what makes this the way that DOES work:

public DataObject[] getDataObjects( )
{
    return (DataObject[]) Data.toArray( new DataObject[ Data.size() ] );
}

I'm not clear on the mechanism at work with casting (or whatever it is) that makes this so.

+10  A: 

Because "toArray()" creates an array of Object, and you can't make Object into DataObject just by casting it. "toArray(DataObject[])" creates an array of DataObject.

And yes, it is a shortcoming of the Collections class and the way Generics were shoehorned into Java. You'd expect that Collection<E>.toArray() could return an array of E, but it doesn't.

Interesting thing about the toArray(DataObject[]) call: you don't have to make the "a" array big enough, so you can call it with toArray(new DataObject[1]) if you like.

I asked a question earlier about Java generics, and was pointed to this FAQ that was very helpful: http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html

Paul Tomblin
Ok. Is there any way to cast Object[] to DataObject[] then? I don't understand what that function exists for, otherwise.Sorry I'm trying to get these stupid questions out of the way now so they don't haunt me later.
Daddy Warbox
I'm afraid there isn't. You just have to use the other toArray call.
Paul Tomblin
No, you can just create a new array and cast your objects one by one. In Java, arrays are hardly ever the way to go, though.
Henning
I see. Thanks for the info.
Daddy Warbox
+2  A: 

To ensure type safety when casting an array like you intended (DataObject[] dataArray = (DataObject[]) objectArray;), the JVM would have to inspect every single object in the array, so it's not actually a simple operation like a type cast. I think that's why you have to pass the array instance, which the toArray() operation then fills.

Henning
+2  A: 

The first way compiles for me in JDK 6 and IntelliJ:

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class DataSet
{
    private Set<DataObject> data;


    public DataSet(DataObject obj)
    {
        this.data = new HashSet<DataObject>();
        data.add(obj);
    }

    public DataSet(DataObject[] objs)
    {
        data = new HashSet<DataObject>();
        data.addAll(Arrays.asList(objs));
    }

    public DataObject[] getDataObjects( )
    {
        return (DataObject[]) data.toArray();
    }
}


class DataObject
{
    // just a dummy to satisfy the compiler.
}

Here are the two implementations from Sun's JDK 6 source code:

        public Object[] toArray() {
            Object[] a = c.toArray();
            for (int i=0; i<a.length; i++)
                a[i] = new UnmodifiableEntry<K,V>((Map.Entry<K,V>)a[i]);
            return a;
        }

        public <T> T[] toArray(T[] a) {
            // We don't pass a to c.toArray, to avoid window of
            // vulnerability wherein an unscrupulous multithreaded client
            // could get his hands on raw (unwrapped) Entries from c.
 Object[] arr = c.toArray(a.length==0 ? a : Arrays.copyOf(a, 0));

            for (int i=0; i<arr.length; i++)
                arr[i] = new UnmodifiableEntry<K,V>((Map.Entry<K,V>)arr[i]);

            if (arr.length > a.length)
                return (T[])arr;

            System.arraycopy(arr, 0, a, 0, arr.length);
            if (a.length > arr.length)
                a[arr.length] = null;
            return a;
        }

If you look in the javadocs for the java.util.Collection interface, you'll see this:

 * <pre>
 *     String[] y = x.toArray(new String[0]);</pre>
 *
 * Note that <tt>toArray(new Object[0])</tt> is identical in function to
 * <tt>toArray()</tt>.
duffymo
Just because it doesn't prevent you from compiling doesn't mean it works. Your code will throw a ClassCastException the first time you call getDataObjects() if the "DataObject" type is different from Object. You should look more closely at the Java library toArray() functions you referred to. The reason there are two of them is that "toArray()" (with no arguments) can only return an Object[], not any other array type. And the other one, "toArray(T[])" exists solely because it can use the type information in its argument to construct its result as the correct T[] type.
newacct