Reputation: 2847
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?
Upvotes: 5
Views: 1223
Reputation: 122489
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();
}
}
Upvotes: 1
Reputation: 2847
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.
Upvotes: 1
Reputation: 31010
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();
}
}
Upvotes: 1
Reputation: 12803
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>();
}
}
Upvotes: 3