Хотя вы не можете буквально делать то, что вы хотите, вы можете подделать его.
- Сделайте все конструкторы вашего класса данных приватными.
- Реализация фабрик / строителей / техников на компаньоне как
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
и другие опасные методы (если таковые имеются, я не проверял) скрыты - но, следовательно, также недоступны, потенциально удаляя некоторые удобные классы данных. .
Выберите свой яд.