Создание собственного неизменяемого постоянного списка Java - проблемы с вводом текста

Я пытаюсь научиться создавать неизменяемый постоянный список. Прямо сейчас моя реализация находится в java, хотя меня больше интересует концепция и выяснение того, как красиво играть со статической типизацией.

Моя первая реализация работала. У него были поля final int size, final T head и final PersistentList<T> tail. Это просто односвязный список, каждый узел указывает на другой узел того же типа PersistentList<T> (через поле tail) или null в случае конца списка. В этом и заключается проблема, я не хочу, чтобы null проверял везде, и не хочу использовать null для представления конца списка/пустого списка. Я хочу представить пустой список с новым объектом EmptyList, который всегда будет последним узлом в моем абстрактном классе PersistentList. Вот что делает clojure и что продемонстрировал Эрик Липперт (на C#).

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

Я создал абстрактный класс IPersistentList и производные от него PersistentList и EmptyList. Цель состоит в том, чтобы иметь следующую структуру списка:

|PersistentList<T>| --> |PersistentList<T>| --> |EmptyList<T>|

Теперь такие методы, как tail или статическая функция create, должны иметь возможность возвращать экземпляр PersistentList<T> или EmptyList<T> в зависимости от того, является ли он последним узлом в списке, но как указать правильный тип возвращаемого значения? Мое предположение состояло в том, чтобы всегда возвращать тип родительского абстрактного класса, IPersistentList<T>. Но это требует, чтобы я всегда приводил производные классы, что кажется ужасно грязным, а компилятор все еще жалуется. Я также не хотел бы решения, в котором клиентский код требуется для инициализации списка типа, а затем для приведения. Я хотел бы, чтобы он был прозрачным.

Вот суть моего кода. Вот ошибка:

persistent_list/src/main/java/com/benreinhart/persistentlist/PersistentList.java:15: error: constructor PersistentList in class PersistentList<T#2> cannot be applied to given types;
    PersistentList<T> p = new PersistentList<T>(head, PersistentList.empty(), 1);
                          ^
  required: T#1,IPersistentList<T#1>,int
  found: T#1,IPersistentList<Object>,int
  reason: actual argument IPersistentList<Object> cannot be converted to IPersistentList<T#1> by method invocation conversion
  where T#1,T#2 are type-variables:
    T#1 extends Object declared in method <T#1>create(T#1)
    T#2 extends Object declared in class PersistentList
Note: persistent_list/src/main/java/com/benreinhart/persistentlist/PersistentList.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
1 error

Как видите, компилятор жалуется на мою попытку приведения от EmptyList<T> к IPersistentList<T>.

В общем, как бы я сделал что-то подобное, учитывая ограничения статической типизации? Есть ли вообще, и если да, то могу ли я сделать это без кастинга везде? Я изо всех сил старался следовать исходному коду clojure для его структур данных, но там так много всего другого, и я довольно плохо знаком с языками со статической типизацией (и java), поэтому я не смог понять все. Это.

Заранее спасибо!


person Ben    schedule 05.07.2014    source источник


Ответы (1)


Проблема в том, что Java не может вывести аргумент типа метода empty(). Он сдается и предполагает, что это Object. EmptyList<Object> нельзя применить к IPersistentList<T>

Чтобы исправить это, вы можете передать его явно:

PersistentList<T> p = new PersistentList<T>(head, PersistentList.<T>empty(), 1);

извлечь empty() во временную локальную переменную:

IPersistentList<T> empty = PersistentList.empty();
PersistentList<T> p = new PersistentList<T>(head, empty, 1);

или используйте Java 8, которая должна справиться с этим случаем.

Дополнительные сведения см. здесь: http://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html#target_types

person Piotr Praszmo    schedule 05.07.2014
comment
Безопасна ли Java 8? Я читал, что они поторопились с этим и признали, что у них не было времени проверить, работает ли он правильно / безопасно. - person David Knipe; 06.07.2014
comment
Спасибо! Ваше решение исправило ошибку, но теперь оно предупреждает меня о непроверенных приведениях. Я получаю эту ошибку (из метода empty()): persist_list/src/main/java/com/benreinhart/persistentlist/PersistentList.java:11: предупреждение: [unchecked] unchecked cast return (IPersistentList‹T›) EMPTY; ^ требуется: IPersistentList‹T› найдено: EmptyList, где T — переменная типа: T extends Объект, объявленный в методе ‹T›empty() 1 предупреждение - person Ben; 06.07.2014
comment
Есть ли способ заставить компилятор думать, что код в порядке (без отключения предупреждений)? Кроме того, есть ли способ выполнить то, что я пытаюсь сделать, без необходимости везде использовать типы? Есть советы, идеи? Спасибо - person Ben; 06.07.2014
comment
Я думаю, что сейчас я добавлю @SuppressWarnings (не проверено), но я бы предпочел, чтобы был способ составить мой список без необходимости постоянного приведения. Начинает казаться, что это невозможно. - person Ben; 06.07.2014
comment
Невозможно сказать, что (IPersistentList<T>) EMPTY безопасен. Проигнорируйте этот случай или вместо этого верните новый экземпляр: new EmptyList<T>(). Другой бросок не нужен. - person Piotr Praszmo; 06.07.2014