Общий переход Kotlin - PullRequest
       3

Общий переход Kotlin

0 голосов
/ 30 октября 2018

Я ищу способ в Котлине обойти дженерики - но они будут иметь фиксированные типы к моменту компиляции.

Моя цель заключается в создании базового класса Field<T>, который будет иметь открытое свойство типа T, которое поддерживается полем ByteArray.

ByteArray будет преобразован во время выполнения в и из T с использованием глобального хранилища преобразователей. Однако я не могу обернуть голову вокруг стирания типа Kotlin, чего я не встречал в C # (почти такой же код работал бы чудесно).

Итак, моей общей целью будет этот класс (обратите внимание, это в основном псевдокод!):

class Field<T> {

    private var actualData: ByteArray = TODO("Init here")
    public var Data: T =
        get() = getConverter().convert(actualData)
        set(value) {
            val converted = getConverter().convertBack(value)
            // Do some other nasty stuff here
            actualData = converted
        }

    private fun getConverter(): Converter<T> = TODO("This needs implementation")
}

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

var data1: Field<Int> = TODO("Init here")
var data2: Field<MyOjbect> = TODO("Init here")

И, конечно же, я бы хотел избежать реализации getConverter() для каждого типа.

Есть ли способ заставить это работать в Kotlin, или я должен придерживаться Java в этой части?

(П.С .: Я, очевидно, не назову свой класс Field<T>, но это имя, похоже, было наиболее общим для описания его роли в этом сценарии).

Ответы [ 2 ]

0 голосов
/ 30 октября 2018

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

Если я понял ваш вопрос и комментарий, вы ищете что-то вроде этого:

Интерфейс преобразователя:

interface Converter<T>{
    fun convert(value: T) : ByteArray
    fun convertBack(byteArray: ByteArray) : T
}

Объект (синглтон), содержащий все конвертеры:

import kotlin.reflect.KClass

object Converters{
    val converters = HashMap<KClass<out Any>, Converter<out Any>>()

    inline fun<reified T : Any> put(converter: Converter<T>){
        converters[T::class] = converter
    }

    fun<T: Any> get(kclass : KClass<T>) : Converter<T>{
        val converter = converters[kclass] ?: throw IllegalStateException("Missing converter for $kclass")
        return converter as Converter<T>
    }

    init {
        //add default converters?
        put(object : Converter<Int>{
            override fun convert(value: Int): ByteArray {
                TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
            }

            override fun convertBack(byteArray: ByteArray): Int {
                TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
            }
        })
    }
}

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

inline fun<reified T : Any> ByteField(initialValue: T) = ByteField(initialValue, T::class)

class ByteField<T: Any>(initialValue: T, private val kclass: KClass<T>){
    private var actualData = converter.convert(initialValue)
    val converter
        get() = Converters.get(kclass)
    var data : T
        get() = converter.convertBack(actualData)
        set(value) {
            actualData = converter.convert(value)
        }
}

Демонстрация использования (конечно, вы также можете реализовать делегаты свойств):

class Demo{
    val intField = ByteField(1)
    val stringField = ByteField("Sfield")
    val doubleField = ByteField(2.0, Double::class) // explicit constructor
}
0 голосов
/ 30 октября 2018

Для этого вам нужно делегированные свойства .

Пример:

import java.nio.ByteBuffer
import java.nio.charset.StandardCharsets
import kotlin.reflect.KProperty

interface Converter<T> {

    fun fromBytes(raw: ByteArray): T

    fun toBytes(typed: T): ByteArray
}

open class MyDelegate<T>(private val converter: Converter<T>) {

    private lateinit var raw: ByteArray

    operator fun getValue(thisRef: Any?, property: KProperty<*>) = converter.fromBytes(raw)

    operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
        raw = converter.toBytes(value)
    }
}

class StringDelegate : MyDelegate<String>(object : Converter<String> {
    override fun fromBytes(raw: ByteArray) = String(raw, StandardCharsets.UTF_8)

    override fun toBytes(typed: String) = typed.toByteArray(StandardCharsets.UTF_8)
})

data class Ints(val first: Int, val second: Int)

class IntsDelegate : MyDelegate<Ints>(object : Converter<Ints> {
    override fun fromBytes(raw: ByteArray) = ByteBuffer.wrap(raw).let { Ints(it.int, it.int) }

    override fun toBytes(typed: Ints) = ByteArray(8).apply {
        with(ByteBuffer.wrap(this)) {
            putInt(typed.first)
            putInt(typed.second)
        }
    }
})

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

class Test {

    var string: String by StringDelegate()

    var ints: Ints by IntsDelegate()

    override fun toString(): String {
        return "string: $string, ints: $ints"
    }
}

fun main(args: Array<String>) {
    val t = Test()

    with(t) {
        string = "first"
        ints = Ints(1, 2)
    }
    println(t) // string: first, ints: Ints(first=1, second=2)

    with(t) {
        string += " + second"
        ints = Ints(ints.first * 2, ints.second * 2)
    }
    println(t) // string: first + second, ints: Ints(first=2, second=4)
}

Обратите внимание, что все это выглядит красиво и элегантно, но может быть довольно дорого. Например. рассмотрим ints = Ints(ints.first * 2, ints.second * 2) - здесь мы выполняем ByteArray -> Ints , обращаемся к свойству first ; выполните еще раз ByteArray -> Ints и получите доступ к свойству second и, наконец, выполните Ints -> ByteArray .

В случае частого доступа к свойствам, особенно если они большие и сложные, это может быть очень дорого.

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