У меня есть проблема при компиляции универсального класса с внутренним классом. Класс расширяет универсальный класс, внутренний класс также.
Здесь интерфейс реализовал:
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>>
^
Что не так? Код компилирует с затмением, но не с компилятором java 5 (я использую муравья с java 5 на Mac и затмеваю 3.5). Нет, я не могу преобразовать его в статический внутренний класс.
Спецификация языка Java, §8.1.3 , определяет семантику подкласса внутренних типов следующим образом:
Кроме того, для каждого суперкласса S из C, который сам является прямым внутренний класс класса SO, существует экземпляр SO, связанный с i, известный как , непосредственно включающий экземпляр i по отношению к S. Непосредственно включающий экземпляр объекта с относительно прямого суперкласса своего класса, если таковой имеется, определяется, когда вызывается конструктор суперкласса {{1 }} через явный оператор вызова конструктора .
Обратите внимание, что включающий экземпляр описывается только как относящийся к определенному классу , а не к конкретному типу . Поскольку все экземпляры универсального типа используют один и тот же класс, следующий код будет допустимым:
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
.
Возможно, это небольшой прогресс, но мне удалось сократить приведенный выше код до следующего кода, который по-прежнему демонстрирует такое же странное поведение:
class Base<E> {
protected class BaseInner<I extends E>{
}
}
class Sub<E> extends Base<E>{
class SubInner extends BaseInner<E> {
}
}