Kotlin通用传递

时间:2018-10-30 13:27:03

标签: generics kotlin

我正在Kotlin中寻找一种方法来传递泛型-但在编译时它们将具有固定的类型。

我的目标是创建一个基类Field<T>,该基类将具有类型T的公共属性,该属性由ByteArray字段支持。

将使用全局转换器存储库在运行时与T进行ByteArray的转换。但是,我无法为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 :(得分:1)

您可以使用reified type parameters,但是与为每种类型显式创建委托相比,它永远都不是编译时安全的,并且会带来运行时开销。

如果我理解您的问题和评论,那么您正在寻找这样的东西:

转换器界面:

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构造函数之上的infline函数,但在调用时不需要显式的类参数:

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
}

答案 1 :(得分:0)

您需要delegated properties

示例:

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

如果您经常访问属性,尤其是当它们很大且很复杂时,可能会花费很大的钱。