Kotlin использует лямбду в качестве инициализатора - PullRequest
1 голос
/ 06 июня 2019

Итак, у меня следующая дилемма. Это вопрос не о том, что не работает, а о том, что было бы более элегантно / полезно и почему.

Итак, мы знаем о init блоках, в них идет логика инициализации. Может быть, вы открываете файл или читаете некоторую константу из файла конфигурации, может быть, вы устанавливаете некоторые свойства на основе более сложного алгоритма, который использует аргументы конструктора.

Однако, что если одно или несколько ваших свойств требуют немного логики, тривиальной логики, такой как проверка аргумента или тому подобное. И эта логика связана только с этим конкретным свойством, без взаимодействия с другими свойствами?

Например:

class MyCircularQueue(k: Int) {

    private val arr = { 
        if (k < 1) 
            throw IllegalArgumentException("k must be at least 1") 
        else  
            Array(k) { 0 }
    }()

    private var head = 0
    private var tail = 0
    private var empty = true

}

Здесь arr необходимо инициализировать в массив 0 с, но, очевидно, существует проблема, если k меньше 1. Требуется действительно короткая логика инициализации, просто простая проверка, которая выдает исключение или инициализирует массив. Никакого взаимодействия с другими свойствами, никакой сложной логики, просто очень простая проверка или логика.

В этом случае мне было интересно, не лучше ли разместить тривиальную логику инициализации в «временном» кодовом блоке (лямбда, вызываемая на месте). Преимущество будет в том, что логика декларации и инициализации будет находиться в одном месте, не разделяться без необходимости, и я думаю, что таким образом она будет более читабельной.

Поэтому после того, как я написал это, я подумал, есть ли у кого-то еще эта проблема из-за очень простой логики инициализации, но я ничего не мог найти по этому поводу.

Итак, суть: все в порядке? Это хорошая практика или, по крайней мере, не плохая практика? Имеет ли это смысл? И есть ли более хороший и / или официальный способ сделать это в Котлине?

Ответы [ 2 ]

4 голосов
/ 06 июня 2019

Обход функции не требуется, if является выражением и возвращает значение, поэтому его можно упростить:

private val arr =
    if (k < 1)
        throw IllegalArgumentException("k must be at least 1")
    else
        Array(k) { 0 }

В качестве альтернативы, Kotlin поставляется с множеством функциональных API в стандартной библиотеке, которые могут помочь в создании кратких строк. В этом случае вы можете сделать:

private val arr =
     k.takeIf { it > 0 }?.let { Array(k) { 0 } } ?: throw IllegalArgumentException("k must be at least 1")
1 голос
/ 06 июня 2019

Как говорится в ответе s1m0nw1, он вам здесь не нужен, но он может быть полезен, когда вы хотите, например, локальные переменные в вашем инициализаторе.

В этом случае вместо того, чтобы использовать () для вызова лямбды, я бы использовал run, который будет красиво встроен и более заметен, например,

private val arr = run {
  val temp1 = ...
  val temp2 = ...
  arrayOf(temp1, temp2)
}
...