Вычисление поля, которое не представлено в конструкторе - PullRequest
3 голосов
/ 06 мая 2019

Допустим, у меня есть класс с 3 полями: A, B и C. Я могу создать экземпляр этого класса двумя способами: передать все поля конструктору или передать только A, и другие свойства будут рассчитаны на основена A.

class MyClass {
    val A: String
    val B: String
    val C: String

    constructor(A: String, B: String, C: String) {
        this.A = A
        this.B = B
        this.C = C
    }

    constructor(A: String) {
        this.A = A
        val (_B, _C) = Calculator.calculate(A)
        this.B = _B
        this.C = _C
    }
}

Этот подход использует два вторичных конструктора без первичного.Но с моей точки зрения это выглядит довольно тяжело.Можно ли переписать эту логику более элегантным способом?

Ответы [ 3 ]

3 голосов
/ 06 мая 2019

Может быть, что-то следующее может быть хорошо для вас. Вы можете заменить Pair фактическим типом, а затем ...

class MyClass(
        val A: String,
        val BC : Pair<String, String>
) {
    constructor(A: String, B: String, C: String) : this(A, B to C) // is this then even needed?
    constructor(A : String) : this(A, Calculator.calculate(A))
}

Или, в качестве альтернативы, если имеет больше смысла иметь три отдельных свойства, наоборот, что также было показано k0enf0rNL :

class MyClass(
     val A: String,
     val B: String,
     val C: String
) {
  constructor(A: String) : this(A, Calculator.calculate(A))
  constructor(A: String, BC : Pair<String, String>) : this(A, BC.first, BC.second)
}

Наконец, если вы не хотите показывать некоторые конструкторы, не стесняйтесь пометить их private, например. следующее будет отображать то же, что и показанный вами пример:

class MyClass(
     val A: String,
     val B: String,
     val C: String
) {
  constructor(A: String) : this(A, Calculator.calculate(A))
  private constructor(A: String, BC : Pair<String, String>) : this(A, BC.first, BC.second)
}

Возможно, вам, скорее, нужно что-то для передачи функции (как вы также использовали Calculator.calculate(A)). Тогда вы можете добавить следующее в качестве конструктора:

constructor(A : String, func: (String) -> Pair<String, String> = Calculator::calculate) : this(A, func(A))

Если это именно то, что вы хотели с самого начала, то даже такой класс, как следующий, может быть чем-то для вас:

class MyClass(val A: String,
              func: (String) -> Pair<String, String> = Calculator::calculate) {
    val B: String
    val C: String

    init {
        func(A).also { (newB, newC) ->
            B = newB
            C = newC
        }
    }
}

Если вы хотите просто передать A, B и C без фактического вычисления (или переданной функции), вы все равно можете сделать это следующим образом (в основном, отбрасывая переданные A):

MyClass("myA") { /* _ -> */ /* A -> */
   "myCalculatedB" to "C"
}
2 голосов
/ 06 мая 2019

Мой личный фаворит - злоупотреблять оператором вызова сопутствующего объекта:

class MyClass(val A: String, val B: String, val C: String) {

    companion object {
        operator fun invoke(A: String): MyClass {
            val (B, C) = Calculator.calculate(A)
            return MyClass(A, B, C)
        }
    }
}
0 голосов
/ 06 мая 2019

Вы можете написать его как класс данных и, по крайней мере, избавиться от конструктора по умолчанию, например

data class MyClass(
    val A: String,
    val B: String,
    val C: String
) {
    constructor(A: String, calculated: Pair<String, String> = calculate()) : this(
        A,
        calculated.first,
        calculated.second
    )
}

fun calculate() : Pair<String, String> = "this" to "that"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...