[英]Kotlin: Mutual assignments
I want to set up two values that hold immutable references to each other. 我想设置两个值,彼此保持不可变的引用。 Example:
例:
data class Person(val other: Person)
val jack = Person(jill), jill = Person(jack) // doesn't compile
Note: lateinit
doesn't seem to work with data class primary constructors. 注意:
lateinit
似乎不适用于数据类主构造函数。
Any ideas? 有任何想法吗?
You could get away with something like this: 你可以逃避这样的事情:
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")
}
}
And then you would be able to do: 然后你就能做到:
val jack = Person()
val jill = jack.other
Using a data class
here does not work for multiple reasons : 在这里使用
data class
不会出于多种原因 :
First because a data class
can't have an empty constructor. 首先是因为
data class
不能有空构造函数。
Even if that wasn't a problem, the generated methods would end up having a cyclic dependency and will fail in runtime with java.lang.StackOverflowError
. 即使这不是问题,生成的方法最终会产生循环依赖,并且会在运行时因
java.lang.StackOverflowError
而失败。 So you'd have to overwrite toString
, equals
, etc. which kind of defeats the purpose of using data class
in the first place. 所以你必须覆盖
toString
, equals
等,这首先打破了使用data class
的目的 。
Here is the trick (note, this is really a trick, you need a good reason to use it in real code). 这是诀窍(注意,这真的是一个技巧,你需要一个很好的理由在实际代码中使用它)。
Unfortunately it won't work with data classes, as they seem to be secured against this kind of hacks. 不幸的是,它不适用于数据类,因为它们似乎可以抵御这种黑客攻击。
But if you have java-stile classes, you may use two things to your advantage: 但是如果你有java-stile类,你可以使用两件事:
val
s in the constructor (same as with final
in java) val
(与java中的final
相同) this
inside the constructor (and you may leak it outside if you really want) this
构造函数中的(并且可能泄漏到外部,如果你真的想) Which means that you can create another Person
inside the constructor of the first person and finalize the creation of both classes before the constructor finishes. 这意味着您可以在第一个人的构造函数内创建另一个
Person
,并在构造函数完成之前完成两个类的创建。
Once again: exposing this
as I did below is a bad idea. 再一次:暴露
this
就像我在下面做的那样是一个坏主意。 When otherFactory
is called, it's parameter is only half-initialized. 当
otherFactory
,它的参数只是半初始化。 This may lead to nasty bugs, especially if you try to publish such reference in multithreaded environment. 这可能会导致令人讨厌的错误,特别是如果您尝试在多线程环境中发布此类引用。
A bit safer approach is to create both Persons inside the constructor of the first Person (you'll need to supply the fields of both entities as arguments). 更安全的方法是在第一个Person的构造函数内创建两个Persons(您需要提供两个实体的字段作为参数)。 It's safer because you're in control of the code that uses half-initialized
this
reference. 它更安全,因为您可以控制使用半初始化
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)
Thanks for your suggestions everybody. 感谢大家的建议。 I came up with an alternative and would like to hear your insights:
我提出了一个替代方案,并希望听到您的见解:
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
Here we have one person lazily instantiating the other on demand, using an internal subclass that implements other
differently (ie it is just given it directly in its constructor). 在这里,我们没有一个人懒洋洋地实例化的其他需求,使用实现内部子类
other
不同的(也就是说,它只是给它直接在它的构造函数)。
Thoughts most welcome. 最受欢迎的想法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.