Search code examples
javacollectionsiteratoriterable

Enhanced For-Loop raises a compilation error with Custom Collection implementing Iterable interface


I'm trying to make a recursive list data structure in Java, similar to lists in functional languages.

I want it to implement Iterable so that it can be used in for-each loops.

So I'me implemented the iterator() method which creates an Iterator, and this loop works fine (list is of type RecursiveList<Integer>):

for (Iterator<Integer> it = list.iterator(); it.hasNext(); ) {
    Integer i = it.next();
    System.out.println(i);
}

Now I was under the impression that for (int i : list) is basically just a syntactic sugar over the for-loop above, but when I try to use the for-each, I'm getting a compilation error:

incompatible types: Object cannot be converted to int

I can't for the life of me figure out why it isn't working. Here's the relevant code:

import java.util.*;

class RecursiveList<T> implements Iterable {

  private T head;
  private RecursiveList<T> tail;
  // head and tail are null if and only if the list is empty
  // [] = { head = null; tail = null}
  // [1,2] = { head = 1; tail = { head = 2; tail = { head = null; tail = null } } }

  public RecursiveList() {
    this.head = null;
    this.tail = null;
  }

  private RecursiveList(T head, RecursiveList<T> tail) {
    this.head = head;
    this.tail = tail;
  }

  public boolean add(T newHead) {
    RecursiveList<T> tail = new RecursiveList<T>(this.head, this.tail);
    this.head = newHead;
    this.tail = tail;
    return true;
  }

  public Iterator<T> iterator() {
    RecursiveList<T> init = this;

    return new Iterator<T>() {
      private RecursiveList<T> list = init;

      public boolean hasNext() {
          return list.head != null;
      }

      public T next() {
          T ret = list.head;
          if (ret == null) throw new NoSuchElementException();
          list = list.tail;
          return ret;
      }
    }
  }
}

class Main {
  public static void main(String[] args) {
    RecursiveList<Integer> list = new RecursiveList<Integer>();

    list.add(1);
    list.add(2);
    list.add(3);

    // works:
    for(Iterator<Integer> it = list.iterator(); it.hasNext();) {
      Integer i = it.next();
      System.out.println(i);
    }
    // output:
    // 3
    // 2
    // 1

    // doesn't work:
    // for (int i : list) System.out.println(i);
  }
}

What's making me feel real stupid is my IDE is catching the problem too and underlining list giving the same error message, so there must be something obviously wrong with how I've written the types that I'm missing, I just can't figure what's happening since iterator() seems to be successfully creating an Iterator instance with the right type based on the more verbose loop working.


Solution

  • Interface Iterable is generic, but your custom Collection implements iterable of row type, which is effectively Iterable<Object>. For that reason, elements retrieved from your collection inside the enhanced for-loop are treated as being of type Object.

    You need to change the declaration of your Collection to:

    class RecursiveList<T> implements Iterable<T>