简体   繁体   English

Kotlin:相互指派

[英]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不会出于多种原因

  1. First because a data class can't have an empty constructor. 首先是因为data class不能有空构造函数。

  2. 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. 所以你必须覆盖toStringequals等,这首先打破了使用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类,你可以使用两件事:

  1. You can initialize val s in the constructor (same as with final in java) 你可以在构造函数中初始化val (与java中的final相同)
  2. You have access to 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.

相关问题 Kotlin/Android 中的“赋值不是表达式”错误 - "Assignments are not expressions" error in Kotlin/Android 分配不是表达式,在这种情况下只能使用表达式-Kotlin - Assignments are not expressions and only expressions are allowed in this context - Kotlin 赋值不是表达式,在这种情况下只允许使用表达式 - Kotlin - Assignments are not expressions, and only expressions are allowed in this context - Kotlin 将Java转换为Kotlin后出现“赋值不是表达式错误” - “Assignments are not expressions error” after converting Java to Kotlin Kotlin分配不是表达式。 还有其他强制断言启用的方法吗? - Kotlin assignments are not expressions. Any other way to force assertions to be enabled? if 语句:赋值不是表达式,在这个上下文中只允许表达式 kotlin - If statement: Assignments are not expressions, and only expressions are allowed in this context kotlin 在 Springboot 中使用 Retrofit (Retrofit2) Client (Kotlin) 的双向 TLS - Mutual TLS using a Retrofit (Retrofit2) Client (Kotlin) within Springboot 赋值不是表达式,在此上下文中只允许使用表达式 - 将Java转换为Kotlin时出错 - Assignments are not expressions, and only expressions are allowed in this context - Error when convert Java to Kotlin 如何在 Kotlin 中的一行中进行多个变量赋值,例如 C,C++? - How to do multiple variable assignments in one line in Kotlin like C,C++? 为什么作业不是陈述 - why assignments are not statements
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM