У меня есть простой класс Kotlin:

data class ValveSpan(val begin:Duration, val end:Duration, val key:String):Comparable<ValveSpan> {
    ...
}

Поскольку это класс данных, я должен иметь основной конструктор с одним или несколькими значениями в нем. И это нормально, мне нравится это краткое выражение.

Но когда я хочу создать вторичный конструктор, который заполняет его из doc-подобного объекта (похожего на JSON), это становится странным:

constructor(doc:TSON):this(doc["begin"].duration!!, doc["end"].duration!!, doc["valves"].sequence!!.first()!!.string!!) {
    // do nothing here, because, uh, I had to do it all in the delegated primary????
}

По сути, цель вторичного конструктора состоит в том, чтобы принять ввод, вычислить некоторые значения для первичного конструктора, а затем вызвать первичный с этими значениями. Но, похоже, мне нужно сделать их встроенными в основной конструктор. Который по мере усложнения вывода становится все более и более сложным. Очень хотелось бы написать что-нибудь вроде:

constructor(doc:TSON) {
    var sanitizedBegin =  doc["begin"].duration ?: 0.minutes
    var sanitizedEnd = doc["end"].duration ?: 0.minutes
    var sanitizedKey = doc["valves"].sequence?.firstOrNull()?.string ?: ""
    primaryConstructor(sanitizedBegin, sanitizedEnd, sanitizedKey)
}

Нет ли возможности делегировать полномочия основному конструктору, не выполняя все вычисления моста на месте?

1
Travis Griggs 24 Сен 2018 в 20:34

2 ответа

Лучший ответ

Чтобы добавить ответ на @ ahmed-ashraf-gamal, вы также можете смоделировать вызов конструктора из клиентов ValveSpan, введя сопутствующий объект, который определяет оператор вызова для сопутствующего объекта. Например:

data class ValveSpan(val begin:Duration, val end:Duration, val key:String) {
    companion object {
        operator fun invoke(doc:TSON): ValveSpan {
            var sanitizedBegin =  doc["begin"].duration ?: 0.minutes
            var sanitizedEnd = doc["end"].duration ?: 0.minutes
            var sanitizedKey = doc["valves"].sequence?.firstOrNull()?.string ?: ""
            return ValveSpan(sanitizedBegin, sanitizedEnd, sanitizedKey)
        }
    }
}

Что тогда позволит вам позвонить ValveSpan(doc).

6
whaley 24 Сен 2018 в 18:12

Для этой цели можно использовать сопутствующий объект:

data class ValveSpan(val begin:Duration, val end:Duration, val key:String) {
    companion object {
        fun fromDoc(doc:TSON): ValveSpan {
            var sanitizedBegin =  doc["begin"].duration ?: 0.minutes
            var sanitizedEnd = doc["end"].duration ?: 0.minutes
            var sanitizedKey = doc["valves"].sequence?.firstOrNull()?.string ?: ""
            return ValveSpan(sanitizedBegin, sanitizedEnd, sanitizedKey)
        }
    }
}

Использование:

ValveSpan.fromDoc(doc)
3
Ahmed Ashraf 24 Сен 2018 в 17:56