Я пытаюсь научиться создавать неизменяемый постоянный список. Прямо сейчас моя реализация находится в 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), поэтому я не смог понять все. Это.
Заранее спасибо!