Как передать значения атрибутов определенного типа без использования конструктора в kotlin (например, оператор распространения в ES6 / TS)? - PullRequest
0 голосов
/ 29 мая 2018

Я часто использую оператор растяжение объекта в ES6 / typcript для передачи значений атрибутов.Например, в машинописи:

interface Control { id?: String, width?: number, height?: number }

function buildTextBoxControl(dataFromDb: any, options: Control) : TextBoxControl {
    return { ...options, text: dataFromDb.name }
}

Тогда я могу вызвать метод, например:

buildTextBoxControl(objFromDb, { height: 45 })
buildTextBoxControl(objFromDb, { width: 25, id: "some-id" })

Как я могу получить эквивалент в kotlin?Иными словами, как метод buildTextBoxControl может быть написан на kotlin?

Ключевым моментом здесь является то, что оператор распространения автоматически устанавливает значения атрибутов из другого объекта, не устанавливая их вручную один за другим.один с использованием котлина apply { }

1 Ответ

0 голосов
/ 07 июня 2019

Вы не можете этого сделать.

Оператор распространения Kotlin (*) ограничен расширением массива Vararg.Из документов :

Когда мы вызываем vararg -функцию, мы можем передавать аргументы один за другим, например, asList(1, 2, 3), или, если у нас уже естьмассив и хотим передать его содержимое в функцию, мы используем оператор распространения (префикс массива с *):

Это ограничивает использование вами оператора распространения.

Кроме того, инициализация на основе скобок не работает, поскольку вместо этого создается функция:

interface Control { 
    var id: String?
    var width: Number?
    var height: Number?
}

class TextBoxControl {
    var id: String? = null
    var width: Number? = null
    var height: Number? = null
    var text: String? = null


}

fun createControl() : TextBoxControl {
    return {};
}

Ошибка: (17, 11) Несоответствие типов: выведенный тип is ()-> Единица, но ожидался TextBoxControl

TL; DR:

Вы не можете инициализировать его с помощью {}.Вы не можете использовать оператор распространения для получения аргументов.


Есть несколько способов решить эту проблему без использования конструкторов, но ни один из них не является гибким (без углубления в размышления, но это падение производительности).

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

interface Control { 
    var id: String?
    var width: Number?
    var height: Number?
}
// Created just to have something to pass as options
class ControlImpl() : Control {
    override var id: String? = "Hello World!"
    override var width: Number? = 314159
    override var height: Number? = 271
}

data class TextBoxControl(var id: String? = null,
    var width: Number? = null,
    var height: Number? = null,
    var text: String? = null)

fun buildTextBoxControl(dataFromDb: String, options: Control) : TextBoxControl {
    return TextBoxControl(id = options.id, width = options.width, height = options.width, text = dataFromDb /*Not entirely what you had, but this is just for the sake of my answer*/)
}
fun main() {
    var control = buildTextBoxControl("String", ControlImpl().apply { 
        id = "id"
        width = 42
        height = 42 
    })
    println(control)
}

TextBoxControl (id = id, width = 42, height = 42, text = String)

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


Примечание: Если вы абсолютно не представляете, какие переменные вы можете получить из объекта управления, и вам все еще нужно использовать все, я настоятельно рекомендую вамперепроектируйте свой код.Ни Kotlin, ни Java не предназначены для «Я дам вам кое-что во время выполнения, и вы должны это выяснить».Самое близкое, что вы получаете к этому - это отражение, но оно все еще ограничено полями, которые у вас есть под рукой.

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

В противном случае, и если вы действительно не хотите использовать конструкторы, у вас есть два варианта:

Установка полей вручную

Если проблема в преобразовании, вы можете броситьв функции для его преобразования:

fun Control.toTextBoxControl() : TextBoxControl{
    return TextBoxControl().let { it ->
        it.id = this.id
        it.width = this.width
        it.height = this.height
        it
    }
}

Теперь вы можете относительно легко преобразовать элемент управления в переопределенный тип:

fun buildTextBoxControl(dataFromDb: String, options: Control) : TextBoxControl {
    return options.toTextBoxControl()
}

Но как насчет дополнительных аргументов?Они были проигнорированы до сих пор.

Если вы на самом деле хотите пойти без конструкторов, вы можете использовать operator fun invoke для изменения параметров:

class TextBoxControl {
    var id: String? = null
    var width: Number? = null
    var height: Number? = null
    var text: String? = null

    operator fun invoke(id: String? = null, width: Number? = null, height: Number? = null, text: String? = null)
            : TextBoxControl{
        if (id != null) this.id = id;
        if (width != null) this.width = width;
        if (height != null) this.height = height;
        if (text != null) this.text = text;
        return this
    }
    override fun toString() : String {
        return "TextBoxControl(id=$id, width: $width=height=$height, text=$text)"
    }
}

Не совсем красиво.Я также столкнулся с проблемой дизайна - я не могу найти достойный способ разрешить использование null с помощью invoke.Если нет нулевой проверки, все исходные переменные будут переопределены.

В любом случае, это позволяет вам изменить функцию преобразования и функцию создания:

fun Control.toTextBoxControl() : TextBoxControl{
    return TextBoxControl()(id = id, width = width, height = height)
}
fun buildTextBoxControl(dataFromDb: String, options: Control) : TextBoxControl {
    return options.toTextBoxControl()(text = dataFromDb)
}

Если вы не знаете, что означает operator fun invoke, это синтаксический сахар для вызова объекта.Так что если у вас есть экземпляр объекта, который имеет operator fun invoke(), вы можете написать someInstanceOfTheClass().Он может иметь аргументы и тип возвращаемого значения, как вы считаете нужным.Вы можете узнать больше о перегрузке операторов в Kotlin здесь .

Отражение

ВНИМАНИЕ: Отражение сильно сказывается на производительности.См. этот вопрос для подробностей об этом.

Бросая карту в интерфейс управления (который возвращает значения в виде карты - также неэффективно из-за нехватки памяти и является еще одной причиной для использования конструкторов), вы можете перебирать карту и использовать отражение для установки значений:

interface Control { 
    var id: String?
    var width: Number?
    var height: Number?

    fun toMap() = mutableMapOf("id" to id, "width" to width, "height" to height) as MutableMap<String, Any>
}
class ControlImpl() : Control {
    override var id: String? = "Hello World!"
    override var width: Number? = 314159
    override var height: Number? = 271
}

class TextBoxControl {
    var id: String? = null
    var width: Number? = null
    var height: Number? = null
    var text: String? = null

    // this could also be a static function called `createTextBoxControlFromMap`, non-static function, or thrown into an apply block or generic extension function
    operator fun invoke(map: Map<String, Any>) : TextBoxControl {
        for((k, v) in map) {
            val f = this::class.java.getDeclaredField(k)
            f.setAccessible(true)
            f.set(this, v)
        }
        return this;
    }
}

fun buildTextBoxControl(dataFromDb: String, options: Control) : TextBoxControl {
    return TextBoxControl()(options.toMap().apply { 
        put("text", dataFromDb) 
    })
}

fun main() {
    var control = buildTextBoxControl("String", ControlImpl().apply { 
        id = "id"
        width = 42
        height = 42 
    })
    println(control)
}

Но, опять же, , пожалуйста, подумайте об использовании конструкторов .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...