Java - локальный класс и дженерики, почему предупреждение компилятора? - PullRequest
5 голосов
/ 21 апреля 2009

Именованные локальные классы используются очень редко, обычно локальные классы являются анонимными. Кто-нибудь знает, почему код ниже генерирует предупреждение компилятора?

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();
  }
}

Предупреждение в new InIterator() и оно говорит

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

Если класс, который не был изменен, сделан анонимным или если он стал членом, предупреждение исчезнет. Однако, как именованный локальный класс, для удаления предупреждения требуется объявление class InIterator<E> implements ....

Что происходит?

Ответы [ 4 ]

3 голосов
/ 21 апреля 2009

Я считаю, что происходит то, что вы игнорируете аргумент универсального типа, называя InIterator без ссылки на универсальный тип в сигнатуре (даже если он присутствует в интерфейсе).

Это относится к категории глупых предупреждений компилятора: вы написали класс так, что 100% InIterator экземпляров будут реализовывать Iterator<E>, но компилятор не распознает его. (Я полагаю, это зависит от компилятора. Я не вижу предупреждения в моем компиляторе Eclipse, но я знаю, что компилятор Eclipse обрабатывает генерики немного иначе, чем компилятор JDK.)

Я утверждаю, что это менее понятно и менее близко к тому, что вы имеете в виду, но, возможно, более дружественно к компилятору и в конечном итоге эквивалентно:

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>();
  }
}
1 голос
/ 21 апреля 2009

Да, я также согласен, что это должно быть ошибкой. Если вы «поднимаете» локальный класс из метода в класс-член, он тоже работает нормально. И между ними нет большой разницы, кроме разных областей видимости и доступа к локальным переменным.

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();
  }
}
1 голос
/ 21 апреля 2009

Теперь я убежден, что это ошибка javac. Приведенные выше решения, которые добавляют универсальный параметр в InIterator, который либо скрывает, либо заменяет E, бесполезны, поскольку они не позволяют итератору делать что-то полезное, например, возвращать элемент типа E - E. Stuff's E.

Однако это компилируется без предупреждений (спасибо Jorn за подсказку):

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>();
  }
}

Определенно ошибка.

1 голос
/ 21 апреля 2009

Хм, здесь предупреждений нет.

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();
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...