Kotlin:是否有可能依赖于实现泛型类型的常量属性?

时间:2016-11-08 12:41:28

标签: generics constants kotlin

我有以下abstrac基类

lapply(mget(paste0("model",1:10)),deviance)

例如,由abstract class Vec2t<T : Number>(open var x: T, open var y: T) { companion object { val SIZE = 2 * when (/** T instance type */) { is Byte, is Ubyte -> 1 is Short, is Ushort -> 2 is Int, is Uint, is Float -> 4 is Long, is Ulong, is Double -> 8 else -> throw ArithmeticException("Type undefined") } } }

实施
Vec2

我想知道是否可以在data class Vec2(override var x: Float, override var y: Float) : Vec2t<Float>(x, y) 中定义SIZE并调用其中一个实现,例如Vec2t

3 个答案:

答案 0 :(得分:4)

您可以在运行时检查x和/或y的类型,并懒惰地初始化size属性:

abstract class Vec2t<T : Number>(open var x: T, open var y: T) {
    val size by lazy {
        2 * when (x) {
            is Byte -> 1
            is Short -> 2
            is Int, is Float -> 4
            is Long, is Double -> 8
            else -> throw ArithmeticException("Type undefined")
        }
    }
}

如果x和/或yfinal,那么您还可以跳过延迟初始化并直接初始化它:

abstract class Vec2t<T : Number>(var x: T, var y: T) {
    val size = 2 * when (x) {
        is Byte -> 1
        is Short -> 2
        is Int, is Float -> 4
        is Long, is Double -> 8
        else -> throw ArithmeticException("Type undefined")
    }
}

答案 1 :(得分:2)

我喜欢建议的解决方案@mfulton26,它很适合这个用例。以下答案仅适用于您不能依赖值的类型的情况(例如TAny,并且您想要确切知道它是什么,但{{1}的所有实例1}}传递给你的班级恰好是T s)。

首先,String中的val不能为您的类的不同实例设置不同的值,因为首先,伴随对象与实例无关,它是一个单独的对象并获取其属性不会(也不能)涉及其封闭类的实例。似乎companion object应该是会员财产。

但是即使对于成员属性和函数,检查类型参数size也不能直接完成,因为Kotlin中的泛型与Java中的泛型类似,并且type erasure也是如此,因此在运行时你不能使用实际类型参数进行操作。

为此,您可以在T中存储KClass<T>(或Class<T>)对象,并根据它实现您的逻辑:

Vec2t<T>

这将要求子类将参数添加到其超类构造函数调用中:

abstract class Vec2t<T : Number>(open var x: T, 
                                 open var y: T, 
                                 private val type: KClass<T>) {
    val size: Int by lazy {
        2 * when (type) {
            Byte::class -> 1
            Short::class -> 2
            Int::class, Float::class -> 4
            Long::class, Double::class -> 8
            else -> throw ArithmeticException("Type undefined")
        }
    }
}

如果您选择此方法,Kotlin还可以向您reified generics寻求帮助,以便您可以避免在使用网站上明确指定class Vec2(override var x: Float, override var y: Float) : Vec2t<Float>(x, y, Float::class) 。例如,如果您的SomeType::class不是抽象的,则可以使用此工厂函数构建它:

Vec2t<T>

使用内联函数,可以只访问实际类型参数,因为函数是在调用站点内联的,因此它的类型参数在编译时始终是已知的。不幸的是,构造函数不能有任何类型参数。

使用方法:

inline fun <reified T: Number> vec2t(x: T, y: T) = Vec2t(x, y, T::class)

答案 2 :(得分:2)

虽然您不能“拥有对于通用类的不同实例化具有不同值的静态字段”(如@yole commented),但您可以定义每个实现及其伴随对象的属性。 e.g:

abstract class Vec2t<T : Number> {
    abstract var x: T
    abstract var y: T
    abstract val size: Int
}

class Vec2f(override var x: Float, override var y: Float) : Vec2t<Float>() {
    companion object {
        const val SIZE = 2 * 4
    }

    override val size: Int
        get() = SIZE
}

class Vec2d(override var x: Double, override var y: Double) : Vec2t<Double>() {
    companion object {
        const val SIZE = 2 * 8
    }

    override val size: Int
        get() = SIZE
}

这允许您在想知道特定实现的大小时引用Vec2f.SIZEVec2d.SIZE等,并在有实例时引用vec2t.size(可能是未知的实现)输入并获得它的大小。