Ошибка компиляции при наследовании универсального внутреннего класса, расширяющегося границами - PullRequest
9 голосов
/ 10 апреля 2010

У меня проблема при компиляции универсального класса с внутренним классом. Класс расширяет универсальный класс, также внутренний класс.

Здесь реализован интерфейс:

public interface IndexIterator<Element>
    extends Iterator<Element>
{
  ...
}

Общий суперкласс:

public abstract class CompoundCollection<Element, Part extends Collection<Element>>
    implements Collection<Element>
{
  ...

  protected class CompoundIterator<Iter extends Iterator<Element>>
      implements Iterator<Element>
  {
    ...
  }
}

Общий подкласс с ошибкой компилятора:

public class CompoundList<Element>
    extends CompoundCollection<Element, List<Element>>
    implements List<Element>
{
  ...

  private class CompoundIndexIterator
      extends CompoundIterator<IndexIterator<Element>>
      implements IndexIterator<Element>
  {
    ...
  }
}

Ошибка:

type parameter diergo.collect.IndexIterator<Element> is not within its bound
       extends CompoundIterator<IndexIterator<Element>>
                                             ^

Что не так? Код компилируется с помощью eclipse, но не компилятором java 5 (я использую ant с java 5 на mac и eclipse 3.5). Нет, я не могу преобразовать его в статический внутренний класс.

Ответы [ 2 ]

8 голосов
/ 10 апреля 2010

Спецификация языка Java, §8.1.3 , определяет семантику внутренних классов подклассов следующим образом:

Кроме того, для каждого суперкласса S С, который сам является прямым внутренним классом класса SO, есть экземпляр ТАК связано с I, известный как немедленно включающий экземпляр я в отношении С. немедленно включающий экземпляр объекта с уважение к своему классу "прямой суперкласс, если таковой имеется, определяется, когда конструктор суперкласса вызывается через явный вызов конструктора заявление.

Обратите внимание, что описываемый экземпляр описывается только как определенный класс , а не конкретный тип . Поскольку все экземпляры универсального типа совместно используют один и тот же класс, следующий код будет допустимым:

class Base<E> {
    E e;

    protected class BaseInner<I extends E>{
        E e() { return e; }
    } 
} 

class StrangeSub extends Base<Integer> {
    protected class StrangeSubInner extends Base<String>.BaseInner<String> {}
}

Конечно, это может быть использовано для разрушения инварианта типа (то есть причина загрязнение кучи ):

    StrangeSub ss = new StrangeSub();
    ss.e = 42;
    String s = ss.new StrangeSubInner().e();

Компилятор eclipse принимает спецификацию языка Java в качестве номинальной стоимости и принимает приведенный выше код, даже не выдавая «непроверенное» предупреждение. Хотя это технически совместимо с JLS, оно явно нарушает его намерения.

Компилятор Sun Java отклоняет объявление StrangeSubInner с:

Test.java:32: type parameter java.lang.String is not within its bound
        protected class StrangeSubInner extends Base<String>.BaseInner<String> {}
                                                                       ^

Видимо, компилятор не просто проверял параметр типа по параметру типа внутреннего суперкласса, связанному с типом eclipse. В этом случае я считаю, что это правильно, поскольку декларация явно небезопасна. Однако компилятор Sun в равной степени отклоняет следующее объявление, даже если оно доказуемо безопасно для типа:

class StrangeSub extends Base<Integer> {
    protected class StrangeSubInner extends BaseInner<Integer> {}
}

Моя догадка заключается в том, что проверка согласованности этих ограничений в форме ромба выходит за пределы возможностей компилятора Sun, и поэтому такие конструкции вместо этого просто отклоняются.

Чтобы обойти это ограничение, я сначала попытался бы избавиться от параметра типа до CompoundIterator.

1 голос
/ 10 апреля 2010

Возможно, это не так много прогресса, но мне удалось сократить приведенный выше код до следующего кода, который все еще демонстрирует то же странное поведение:

class Base<E> { 
    protected class BaseInner<I extends E>{
    } 
} 

class Sub<E> extends Base<E>{ 
    class SubInner extends BaseInner<E> { 
    }
} 
...