假设我有一个这样的数据类:
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/