Question

Named local classes are very rarely used, usually local classes are anonymous. Does anybody know why the code below generates a compiler warning?

public class Stuff<E> {
  Iterator<E> foo() {
    class InIterator implements Iterator<E> {
      @Override public boolean hasNext() { return false; }
      @Override public E next() { return null; }
      @Override public void remove() { }
    }
    return new InIterator();
  }
}

The warning is in new InIterator() and it says

[unchecked] unchecked conversion
found   : InIterator
required: java.util.Iterator<E>

If the class, unchanged, is made anonymous, or if it is made a member, the warning goes away. However, as a named local class, it requires a declaration class InIterator<E> implements ... for the warning to go away.

What's going on?

Was it helpful?

Solution 3

I am now convinced that it's a javac bug. The solutions above that add a generic parameter to InIterator that either hides or replaces E aren't helpful, because they preclude the iterator from doing something useful, like returning an element of type E - Stuff's E.

However this compiles with no warnings (thanks Jorn for the hint):

public class Stuff<E> {
  E bar;
  Iterator<E> foo() {
    class InIterator<Z> implements Iterator<E> {
      @Override public boolean hasNext() { return false; }
      @Override public E next() { return bar;  }
      @Override public void remove() { }
    }
    return new InIterator<Void>();
  }
}

Definitely a bug.

OTHER TIPS

I believe what's happening is you are ignoring the generic type argument by naming InIterator without a reference to the generic in the signature (even though it is present in the interface).

This falls into the category of silly compiler warnings: you've written the class so that 100% InIterator instances will implement Iterator<E>, but the compiler doesn't recognize it. (I suppose this depends on the compiler. I don't see the warning in my Eclipse compiler, but I know that the Eclipse compiler handles generics slightly differently than the JDK compiler.)

I argue that this is less clear, and less close to what you mean, but perhaps more compiler friendly, and ultimately equivalent:

public class Stuff<E> {
  Iterator<E> foo() {
    class InIterator<F> implements Iterator<F> {
      @Override public boolean hasNext() { return false; }
      @Override public E next() { return null; }
      @Override public void remove() { }
    }
    return new InIterator<E>();
  }
}

Hmm, no warnings here.

import java.util.Iterator;

public class Stuff<E> {
    Iterator<E> foo() {
        class InIterator implements Iterator<E> {
            public boolean hasNext() {
                return false;
            }

            public E next() {
                return null;
            }

            public void remove() {
            }
        }
        return new InIterator();
    }

    public static void main(String[] args) {
        Iterator<String> i = new Stuff<String>().foo();
    }
}

Yeah, I also agree that this should be a bug. If you "lift" the local class out of the method into a member class, it works fine too. And there isn't much difference between the two except different scoping and access to local variables.

public class Stuff<E> {
  class InIterator implements Iterator<E> {
    @Override public boolean hasNext() { return false; }
    @Override public E next() { return null; }
    @Override public void remove() { }
  }
  Iterator<E> foo() {
    return new InIterator();
  }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top