views:

245

answers:

4

We're getting this error

java.lang.NullPointerException
    at java.util.ArrayList.<init>(Unknown Source)
    at de.mystuff.ExtendedArrayList.<init>(ExtendedArrayList.java:38)

where ExtendedArrayList:38 is

new ArrayList(new ArrayCollection<E>(data));

In short: the ArrayList constructor sometimes seems to choke on our home grown Collection implementation ArrayCollection.

I was unable to reproduce it on my computer even with the exact same version that was distributed to our customers.

But i'm not 100% sure that they are using the JRE we included. So, i googled for some ArrayList.java source code and found openJDK 6b17 which has this

public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    size = elementData.length;
    // c.toArray might (incorrectly) not return Object[] (see 6260652)
    if (elementData.getClass() != Object[].class)
        elementData = Arrays.copyOf(elementData, size, Object[].class);
}

That would make sense, because if there's no data our ArrayCollection.toArray() returns null. And this constructor looked safe (and worked without exception) for the 1.5.0_09 Sun JDK/JRE implementation we are using.
But openJDK only seems to release for the unix world. Is this code also part of a Windows JRE? And if so, which version?

NB: I know that we have to fix our classes, but i want to make sure i understand the cause of the NullPointerException.

+7  A: 

Why do you think that it's legitimate to return null rather than returning a zero-length array?

The javadoc for List does not allow for this. So, the cause is that all the other JRE's make the same assumption. The Sun implementation's published sources on my Mac certainly make that assumption.

bmargulies
What he said. +1, except I'm out of votes for today.
Carl Smotricz
Not really a helpful answer.
Tim Drisdelle
@Carl Smotricz: who's side are you on :-)? Are comments reordered?
bmargulies
Good answer, because I have my doubt that a "home grown" implementation of ArrayList makes any sense in the first place. And if, why not reading API carefully enough before using "extend"?
pimpf0r
... and actually my question is about understanding the problem - not about how to fix it or prevent it in the first place.
Stroboskop
thus my edit to clarify, or so I hoped.
bmargulies
+1  A: 

According to J2SE 5.0 API Documentation:

ArrayList public ArrayList(Collection c) Constructs a list containing the elements of the specified collection, in the order they are returned by the collection's iterator. The ArrayList instance has an initial capacity of 110% the size of the specified collection.

Parameters: c - the collection whose elements are to be placed into this list.

Throws:
NullPointerException - if the specified collection is null.

So

new ArrayList(new ArrayCollection<E>(data));

throws NullPointerException if new ArrayCollection<E>(data) is null.

Edit:

140     /**
141      * Constructs a list containing the elements of the specified
142      * collection, in the order they are returned by the collection's
143      * iterator.
144      *
145      * @param c the collection whose elements are to be placed into this list
146      * @throws NullPointerException if the specified collection is null
147      */
148     public ArrayList(Collection<? extends E> c) {
149     elementData = c.toArray();
150     size = elementData.length;
151     // c.toArray might (incorrectly) not return Object[] (see 6260652)
152     if (elementData.getClass() != Object[].class)
153         elementData = Arrays.copyOf(elementData, size, Object[].class);
154     }

OpenJDK has these lines in ArrayList constructor. According to the documentation of this constructor: 146 * @throws NullPointerException if the specified collection is null

It should only throw an exception if collection is null.

Here these lines:

149     elementData = c.toArray();
150     size = elementData.length;

Collection.toArray().length method is called.

In your implementation Collection.toArray() is null so it throws a NullPointerException.

According to J2SE Collection.toArray documentation

toArray Object[] toArray() Returns an array containing all of the elements in this collection. If the collection makes any guarantees as to what order its elements are returned by its iterator, this method must return the elements in the same order.

The returned array will be "safe" in that no references to it are maintained by this collection. (In other words, this method must allocate a new array even if this collection is backed by an array). The caller is thus free to modify the returned array.

This method acts as bridge between array-based and collection-based APIs.

Returns: an array containing all of the elements in this collection

So it should not return null. It should return an empty array. So .length will return 0 and there will be no problem.

JCasso
um... yes. but "new something" is _never_ null.
Stroboskop
Well if `new ArrayCollection<E>(data)` does not throw exception then **according** to the API Reference `new ArrayList(new ArrayCollection<E>(data));` should not throw exception either. It will initialize an ArrayList and if collection has 0 elements then the ArrayList will have 0 elements either. That means any true implementation will not throw a NullPointerException here.
JCasso
yep. that's what this is about: differences in the ArrayList implementations that might cause the Exception.
Stroboskop
Well i got the problem and edited my answer.
JCasso
+1  A: 

The List#toArray method should never return null. Your implementation of ArrayCollection may have overriden the toArray methods and may be returning null for empty collections instead of a new empty array.

And yes, it is possible that different JRE's behave differently. Some may tolerate null instead of an array while others don't.

I'd fire up my Java decompiler and have a look at the actual implementation of this ArrayList constructor.

Andreas_D
+1  A: 

I don't think you specified exactly which JRE version/vendor you are using, but here is the source for the constructor in question for Sun JDK 1.6.0_17 (if you aren't already aware, the source for most classes in the java namespace ships with the JDK):

public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    size = elementData.length;
    // c.toArray might (incorrectly) not return Object[] (see 6260652)
    if (elementData.getClass() != Object[].class)
         elementData = Arrays.copyOf(elementData, size, Object[].class);
}

(the comment is the author's, not mine)

This should pretty clearly show the potential cause of a NPE in this constructor: if c is null or (in your case) if c.toArray() returns null.

matt b
That confirms my suspicion that our clients tweaked our installation with a more recent JRE. I hadn't thought of checking with a 1.6 JRE yet.
Stroboskop
Here is the source from a 1.5.0_20 JDK: http://pastebin.com/f2710e36c In that version the constructor checks the size with c.size(), not c.toArray().length
matt b