Intereting Posts
Kotlin to Java (Справка библиотеки) Varargs Kotlin Java interop не работает должным образом Фрагмент не в котлин, а в java он работает (тот же код) Spring Boot не запускает автозагрузчик при использовании @Cacheable kotlin map добавляет значение, если не равно Как сортировать на основе / сравнивать несколько значений в Котлин? Как преобразовать / преобразовать коллекцию в другую коллекцию по свойству элемента? Котлин: Итерации через JSONArray Kotlin – Самый идиоматический способ преобразования списка в MutableList Переменная вида Kotlin неожиданно null в методе Activity onCreate Расширенное назначение и приращение не поддерживаются для локальных делегированных свойств и встроенных свойств Как использовать anko spinner? Как получить доступ к статическому методу Java в подклассе Kotlin? kotlin, используя тип генериков класса данных Комната не может выбрать конструктор, поскольку подходящие ошибки являются множественными конструкторами

Как установить значение свойства по умолчанию для Kotlin для `this`

У меня есть следующая структура класса, которая представляет собой простое дерево. Каждый элемент может иметь несколько дочерних элементов и родителя.

Однако корень дерева вызывает у меня головную боль. Я пытаюсь сделать это без использования null чтобы я мог пересекать дерево вверх, вызывая item.parent . Чтобы упростить это, я хочу, чтобы корень имел себя как родитель, но я не могу понять, как это сделать.

 interface Item { val parent: Directory } interface ItemWithChildren{ val children: MutableList<Item> } class Directory() : Item, ItemWithChildren { override val children: MutableList<Item> = mutableListOf() override val parent: Directory by lazy { this } constructor(par: Directory) : this() { parent = par //Error: val cannot be reassigned } } class File(override val parent: Directory) : Item 

Этот код не компилируется, потому что невозможно переназначить val parent . Но использование this параметра как значения по умолчанию также невозможно. Есть ли выход?

Если я разрешаю родительскому элементу быть нулевым, то решение легко. Но я не хочу использовать нули, если это возможно. Также null item.parent цепочку item.parent .

Вы можете использовать блок init . например:

 class Directory(parent: Directory? = null) : Item, ItemWithChildren { override val children: MutableList<Item> = mutableListOf() override val parent: Directory init { this.parent = parent ?: this } } 

В качестве альтернативы вы можете создать отдельную «родительскую» реализацию для «root». например:

 interface ChildItem /* renamed from `Item` for clarity */ { val parent: ParentItem } interface ParentItem /* renamed from `ItemWithChildren` for clarity */ { val children: MutableList<ChildItem> } class Root() : ParentItem { override val children: MutableList<ChildItem> = mutableListOf() } class Directory(override val parent: ParentItem) : ChildItem, ParentItem { override val children: MutableList<ChildItem> = mutableListOf() } class File(override val parent: ParentItem) : ChildItem 

Таким образом, ваш «корневой» элемент не имеет parent свойства, похожего на то, как ваши объекты «лист» («файл») не имеют children свойства. Вероятно, вы также захотите, чтобы ваши интерфейсы ChildItem и ParentItem расширяли общий интерфейс (например, named Item ).

@ mfulton26 ответил, как это сделать так, как вы строго запрашиваете. Но для других, которые могут задаться вопросом об этом выборе, они все равно должны также считать, что для этого типа работы в Котлине есть null значения.

У вас может быть свойство null и несколько производных свойств, которые позволяют получить доступ как null . Потому что в любом случае (ваш план избежать null или принятия и использования null ) вам придется спросить « у меня есть родитель? », Который почти такой же, как запрос « является родительским null? ». Так почему же необычный «возможно бесконечный цикл, вызывающий «обход» для этого случая?

Если бы мой древовидный класс был чем-то вроде:

 data class Something(val text: String, val parentOrNull: Something? = null) { val parent: Something get() = parentOrNull!! val hasParent = parentOrNull != null } 

Затем у меня есть варианты доступа к родителям с учетом и без беспокойства по поводу null :

 val root = Something("rooty") val child = Something("youngun", root) val leaf = Something("baby", child) fun printPathToNode(node: Something) { // use derived properties to check and not worry about the null if (node.hasParent) printPathToNode(node.parent) println(node) } fun findRoot(node: Something): Something { // use null operators to not worry about the null return node.parentOrNull?.let { findRoot(it) } ?: node } 

Тогда вы можете видеть, что он работает отлично с хорошим выходом, и никаких проблем с null :

 printPathToNode(leaf) // rooty, youngun, baby printPathToNode(child) // rooty, youngun printPathToNode(root) // rooty println(findRoot(leaf)) // rooty println(findRoot(child)) // rooty println(findRoot(root)) // rooty 

Нулей следует избегать в тех случаях, когда они не имеют смысла. Но иногда это действительно разумный вариант. Kotlin помогает защитить вас, когда у вас есть значения NULL, а также, зная о них, а не просто притворяясь, что все в порядке. И тогда это дает вам отличные операторы с недействительностью, которые помогут вам в работе с ними.

Вот как я буду использовать ответ @ mfulton:

 class Directory(parent: Directory? = null) : Item, ItemWithChildren { override val children = mutableListOf<Item>() override val parent = parent ?: this }