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

时间:2018-06-28 08:01:09

标签: 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" }
    }
}

我希望能够在调用somethingElse方法之前将功能应用于init。在这种情况下,我想从\n字符串中删除所有somethingElse个字符,同时保持字段的不变性(即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中,惯用的方式是什么?

1 个答案:

答案 0 :(得分:0)

虽然您不能按字面意思做您想做的事,但是您可以伪造它。

  • 将数据类的所有构造函数设为私有。
  • operator fun invoke来实现工厂/建造者/同伴上的一切。

Companion.invoke的用法-在科特林! -就像构造函数调用一样。

在您的示例中:

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)
}

打印:

  

这种邪恶的东西来了

您会注意到有帮助的警告:

  

私有数据类构造函数通过生成的“ copy”方法公开。

此方法不能被覆盖(据我所知),因此您仍然必须保重。一种解决方案是隐藏实际的数据类:

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和其他危险方法(如果有的话,我没有检查过)被隐藏起来-但也无法使用,有可能删除一些便利数据类提供。

选择你的毒药。