constructor - 如何在 init 方法之前将函数应用于数据类构造函数中定义的值?

标签 constructor kotlin invariants data-class

假设我有一个这样的数据类:

data class MyData(val something: Int, val somethingElse : String) {
    init {
        require(something > 20) { "Something must be > 20" }
        require(StringUtils.isNotEmtpy(somethingElse)) { "Something else cannot be blank" }
    }
}

我希望能够在调用 init 方法之前将函数应用于 somethingElse。在这种情况下,我想从 somethingElse 字符串中删除所有 \n 字符,同时保持字段的不变性(即 somethingElse 必须仍然是 val)。我想在 Java 中做类似的事情:

public class MyData {

    private final int something;
    private final String somethingElse;

    public MyDate(int something, String somethingElse) {
        this.something = something;
        this.somethingElse = StringUtils.replace(somethingElse, '\n', '');

        Validate.isTrue(something > 20, "...");
        Validate.isTrue(StringUtils.isNotEmtpy(this.somethingElse), "...");
    }

    // Getters
}

我当然可以在 Kotlin 中创建一个普通类(即没有数据类),但我希望 MyData 成为一个数据类。

在 Kotlin 中执行此操作的惯用方法是什么?

最佳答案

虽然您不能从字面上做您想做的事,但您可以伪装它。

  • 将数据类的所有构造函数设为私有(private)。
  • 作为operator fun invoke在伙伴上实现工厂/ builder /任何东西。

Companion.invoke 的用法将——在 Kotlin 中! -- 看起来就像构造函数调用。

在你的例子中:

data class MyData private constructor(
    val something: Int, 
    val somethingElse : String
) {
    init {
        require(something > 20) { "Something must be > 20" }
        require("" != somethingElse) { "Something else cannot be blank" }
    }

    companion object {
        operator fun invoke(something: Int, somethingElse: String) : MyData =
            MyData(something, somethingElse.replace("\n", " "))
    }
}

fun main(args: Array<String>) {
    val m = MyData(77, "something\nwicked\nthis\nway\ncomes")
    println(m.somethingElse)
}

打印:

something wicked this way comes

您会注意到有用的警告:

Private data class constructor is exposed via the generated 'copy' method.

这个方法不能被覆盖(据我所知)所以你仍然要小心。一种解决方案是隐藏实际的数据类:

interface MyData {
    val s: Int
    val sE: String

    private data class MyDataImpl(
        override val s: Int,
        override val sE: String
    ) : MyData {
        init {
            require(s > 20) { "Something must be > 20" }
            require("" != sE) { "Something else cannot be blank" }
        }
    }

    companion object {
        operator fun invoke(s: Int, sE: String) : MyData =
                MyDataI(s, sE.replace("\n", " "))
    }
}

现在您的不变量(没有换行符)得到维护,copy 和其他危险方法(如果有的话,我还没有检查过)被隐藏起来——但因此也不可用,可能会删除一些方便的数据类提供。

选择你的毒药。

关于constructor - 如何在 init 方法之前将函数应用于数据类构造函数中定义的值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51077686/

相关文章:

python - 如何在Python中具有相同参数的X子方法中实例化类X?

java - 如何从 okhttp3.ResponseBody 获取 url?

gradle - 使用 Gradle Kotlin DSL 进行集成测试

c++ - 在 RAII 中,持有资源是类不变性意味着什么?

algorithm - 终止函数定义(算法)

ada - 使用 SPARK 证明选择排序算法

c++ - 使用继承时构造函数/析构函数调用的顺序

javascript - 重写 JS 字符串构造函数 : Am I Way Off?

android - Fragment 是无限创造的

python - 在 Python 中使用 super() 关键字的单继承的基本示例是什么?