Kotlin:是否有可能有一个恒定的属性依赖于实现泛型?

我有以下abstrac基类

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) 

我想知道是否可以在Vec2t定义SIZE并调用它的一个实现,例如Vec2.SIZE

虽然你不能“拥有静态字段,这些静态字段对于泛型类的不同实例有不同的值”(如@yole所 评论的 ),但是你可以在每个实现及其伴随对象上定义属性。 例如:

 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

您可以在运行时检查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") } } 

我喜欢解决方案@ mfulton26建议,它非常适合这个用例。 下面的答案是用于不能依赖值的类型的情况(例如TAny ,而且你想确切地知道它是什么,但是所有传递给你的类的T实例都是String )。


首先,对于类的不同实例, companion object中不能有val ,因为首先,伴随对象与实例无关,它是一个单独的对象,并且它的属性没有(而且不能)涉及其封闭类的实例。 似乎size应该是一个成员属性。

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

为此,您可以在您的Vec2t<T>存储一个KClass<T> (或者Class<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") } } } 

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

 class Vec2(override var x: Float, override var y: Float) : Vec2t<Float>(x, y, Float::class) 

如果您选择这种方法,Kotlin也可以为您提供泛化泛型 ,以避免在使用地点明确指定SomeType::class 。 例如,如果你的Vec2t<T>不是抽象的,你可以用这个工厂函数来构造它:

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

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

用法:

 val i = vec2t(1, 1) // Vec2t<Int> inferred from arguments type Int println(i.size) // 8 val d = vec2t(1.0, 1.0) // the same but it's Vec2t<Double> this time println(d.size) // 16