views:

1595

answers:

6

In Java, I need to return an Iterator from my method. My data comes from another object which usually can give me an iterator so I can just return that, but in some circumstances the underlying data is null. For consistency, I want to return an "empty" iterator in that case so my callers don't have to test for null.

I wanted to write something like:

public Iterator<Foo> iterator() {
   if (underlyingData != null) {
      return underlyingData.iterator();  // works
   } else {
      return Collections.emptyList().iterator();  // compiler error
   }
}

But the Java compiler complains about the returning Iterator<Object> instead of Iterator<Foo>. Casting to (Iterator<Foo>) doesn't work either.

+15  A: 

You can get an empty list of type Foo through the following syntax:

return Collections.<Foo>emptyList().iterator();
Alex B
I have just discovered that sometimes trying to be quick slows you down. I had written up an empty Iterator<Foo> implementation and returned here to find I'd been off on a bunny trail. +1
Michael Myers
A: 

Sorry, I figured it out. You need to use an assignment so that the compiler can figure out the parameterized type.

public Iterator<Foo> iterator() {
   if (underlyingData != null) {
      return underlyingData.iterator();
   } else {
      List<Foo> empty = Collections.emptyList();  // param type inference
      return empty.iterator();
   }
}
miner49r
Wow, Alex B answered even before I could type in my solution. I was missing the syntax for the parameterized type returned by emptyList(). The assignment works, but the one liner is even better.
miner49r
A: 

Try this

public Iterator<Foo> iterator() {
   if (underlyingData != null) {
      return underlyingData.iterator();  // works
   } else {
      return Collections.<Foo>emptyList().iterator();  // works now
   }
}
e5
+7  A: 

I'd go more along the lines of

public Iterator<Foo> iterator() {
    if (underlyingData == null)
     return Collections.<Foo> emptyList().iterator();
    return underlyingData.iterator();
}

Just, handle the special case and return, then handle the normal case. But my main point is that you can avoid the assignment with

Collections.<Foo> emptyList().iterator();
Carl Manaster
+1 for handling only the special case and removing 'else'
Grant Wagner
IMO, null and non-null are equally special in this case. else is clearer here.
Tom Hawtin - tackline
You're entitled to YO, of course, but to me it seems as if this class is treating all lists, including the empty list, as if (this class) were the list - that is normal flow, to me - but it is treating null specially - as if it were the empty list, which it is not. It is delegating to underlyingData normally, and manufacturing something special when it can't.
Carl Manaster
-1 (or at least a warning) for not using braces.
DJClayworth
@DJClayworth, you're entitled to YO, as well. I consider unnecessary braces to be codejunk. http://www.slideshare.net/carlmanaster/codejunk-ignitesd
Carl Manaster
+2  A: 

I guess this shows that Java type inference doesn't work in every case and that the ternary operator is not always equivalent to the apparently equivalent if-else construct.

I also want to state avoid null. Also avoid passing Iterators about, because they have strange stateful behaviour (prefer Iterable). However, assuming you have a legitimate, non-premature reason for doing this, my preferred way of writing it would be

public Iterator<Foo> iterator() {
    return getUnderlyingData().iterator();
}
private List<Foo> getUnderlyingData() {
    if (underlyingData == null) {
        return Collections.emptyList();
    } else {
        return underlyingData;
    }
}

IMO, it's better not to insert the inferable type information if it can be inferred (even if it makes your code longer).

You are almost certainly going to do it more than once, so insert a getUnderlyingData method instead of just declaring a local variable.

You are calling iterator on both results, so do not repeat yourself.

Tom Hawtin - tackline
+1 for the DRY win from only calling iterator() from one place. I wonder if this kind of solution could be moved to where underlyingData is modified - this would be DRYest of all I think.
CurtainDog
+2  A: 

The annoyance of Collections.<Foo>emptyList().iterator() is the main reason we provide Iterators.emptyIterator() in google-collections. No type parameter needed, in cases like yours.

Kevin Bourrillion