Котлин: взаимные задания - PullRequest
0 голосов
/ 11 июня 2018

Я хочу установить два значения, которые содержат неизменные ссылки друг на друга.Пример:

data class Person(val other: Person)
val jack = Person(jill), jill = Person(jack)   // doesn't compile

Примечание: lateinit, похоже, не работает с первичными конструкторами класса данных.

Есть идеи?

Ответы [ 3 ]

0 голосов
/ 11 июня 2018

Вот хитрость (заметьте, это действительно хитрость, вам нужна веская причина, чтобы использовать ее в реальном коде).

К сожалению, она не будет работать с классами данных, так как они кажутсяЗащищено от такого рода хаков.

Но если у вас есть классы Java-стиля, вы можете использовать две вещи в своих интересах:

  1. Вы можете инициализировать val s в конструкторе(так же, как с final в Java)
  2. У вас есть доступ к this внутри конструктора (и вы можете утечь его снаружи, если вы действительно хотите)

Это означает, чтовы можете создать еще один Person внутри конструктора от первого лица и завершить создание обоих классов до его завершения.

Еще раз: показ this, как я сделал ниже, плохая идея.Когда вызывается otherFactory, его параметр инициализируется только наполовину.Это может привести к неприятным ошибкам, особенно если вы попытаетесь опубликовать такую ​​ссылку в многопоточном окружении.

Немного более безопасный подход - создать обоих Person внутри конструктора первого Person (вам нужно будет указать поляобеих сущностей в качестве аргументов).Это безопаснее, потому что вы контролируете код, который использует половину инициализированную ссылку this.

class Person {
    val name: String
    val other: Person

    constructor(name: String, other: Person) {
        this.name = name
        this.other = other
    }

    // !! not very safe !!
    constructor(name: String, otherFactory: (Person) -> Person) {
        this.other = otherFactory(this)
        this.name = name
    }

    // a bit safer
    constructor(name: String, otherName: String) {
        this.other = Person(otherName, this)
        this.name = name
    }
}

val person1 = Person("first") {
    Person("second", it)
}

val person2 = person1.other

print(person1.name) // first
print(person2.name) // second

val person3 = Person("third", "fourth")
val person4 = person3.other

print(person3.name)
print(person4.name)
0 голосов
/ 12 июня 2018

Спасибо всем за ваши предложения.Я предложил альтернативу и хотел бы услышать ваши идеи:

open class Person {
  open val other: Person by lazy { Person2(this) }
  class Person2(override val other: Person): Person()
}

val jack = Person()
val jill = jack.other

Здесь один человек лениво создает другой экземпляр по требованию, используя внутренний подкласс, который реализует other по-разному (т.е. это простодано прямо в конструкторе).

Мысли приветствуются.

0 голосов
/ 11 июня 2018

Вы можете получить что-то вроде этого:

class Person() {
    private var _other: Person? = null

    private constructor(_other: Person? = null) : this() {
        this._other = _other
    }

    val other: Person
        get() {
            if (_other == null) {
                _other = Person(this)
            }
            return _other ?: throw AssertionError("Set to null by another thread")
        }
}

И тогда вы сможете сделать:

val jack = Person()
val jill = jack.other

Использование data class здесь не работает для несколько причин :

  1. Сначала, потому что data class не может иметь пустой конструктор.

  2. Даже если бы это не было проблемой, сгенерированные методы в конечном итоге имели бы циклическую зависимость и не работали бы во время выполнения с java.lang.StackOverflowError.Таким образом, вам придется перезаписать toString, equals и т. Д., Какой тип побеждает цель использования data class в первую очередь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...