I like the solution @mfulton26 suggested, it fits this use case very well. The below answer is rather intended for the cases when you cannot rely on the types of the values (e.g. T
is Any
, and you want to know exactly what it is, but all the instances of T
passed to your class happened to be String
s).
First, you cannot have a val
in a companion object
that has different values for different instances of your class, because, first of all, the companion object has nothing to do with the instances, it's a separate object and getting its property doesn't (and cannot) involve instances of its enclosing class. Seems like size
should be a member property.
But even for member properties and functions, inspecting the type argument T
cannot be done directly, because generics in Kotlin are similar to those in Java and have type erasure too, so that at runtime you cannot operate with actual type arguments.
To do that, you can store a KClass<T>
(or Class<T>
) object in your Vec2t<T>
and implement your logic based on it:
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")
}
}
}
This would require the subclasses to add the argument to their superclass constructor call:
class Vec2(override var x: Float, override var y: Float) : Vec2t<Float>(x, y, Float::class)
If you choose this approach, Kotlin can also offer you reified generics for help, so that you can avoid explicitly specifying SomeType::class
at use site. For example, if your Vec2t<T>
was not abstract, you could construct it with this factory function:
inline fun <reified T: Number> vec2t(x: T, y: T) = Vec2t(x, y, T::class)
With inline functions, you can access actual type arguments only because the function is inlined at call sites, thus its type arguments are always known at compile-time. Unfortunately, constructors cannot have any type parameters.
With usage:
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