2016-11-08 62 views
2

我有以下abstrac基類Kotlin:是否有可能具有一個依賴於實現泛型類型的常量屬性?

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

+0

'size'屬性不應位於伴隨對象中。你不能訪問'length()'函數。在創建對象之前,您無法測量其大小。 – marstran

+0

那麼尺寸應該放在哪裏?我發現它可能絕對是靜態的。對不起'length()',它是一個wip。 – elect

+0

@elect,似乎它不應該是靜態的,因爲你希望大小對於Vec2t的不同實例是不同的 - 意味着它的行爲應該根據它所調用的內容而有所不同,絕對不是靜態的行爲。 – hotkey

回答

2

儘管您不能「爲靜態字段具有不同值的泛型類的不同實例」(如@yolecommented),但您可以爲每個實現及其伴隨對象定義屬性。例如:

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當你有(可能是未知的實現類型)的實例,並得到其大小。

+0

這正是我最終做的。 – elect

2

我喜歡解決方案@mfulton26建議,它符合這個用法ca非常好。以下答案適用於不能依賴數值類型的情況(例如,TAny,並且您想確切知道它是什麼,但傳遞給您班級的所有實例都是String s )。


首先,你不能在一個companion object有你的類的不同實例的不同值val,因爲,首先,同伴對象有無關的情況下,它是一個獨立的對象並且獲得其財產不(也不能)涉及其封閉類的實例。看起來像size應該是一個成員屬性。

但即使是成員的屬性和功能,檢查類型參數T不能直接做,因爲在科特林泛型是類似於在Java和有type erasure太多,因此在運行時你不能用實際類型參數進行操作。

要做到這一點,您可以根據其存儲在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") 
     } 
    } 
} 

這就要求子類的參數添加到他們的超類的構造函數調用:

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

如果選擇這種方式,可以科特林還爲您提供reified generics幫忙,所以您可以避免在使用地點明確指定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 
+0

我可能會忽略一些東西,但我相信添加'type'因爲'sizeOfVec2t()'是內聯的,所以它的'reified'可以避免'Vec2t'來代替包級函數:'inline fun sizeOfVec2t()= 2 * ...' – mfulton26

+0

@ mfulton26 T'只能由呼叫地點的靜態/推斷類型決定。例如,'(intVec as Vec2t ).sizeOfVec2t()'將無法發現'intVec'對'T'具有'Int'。 – hotkey

+0

啊,是的,是的,我想我可能會忽略一些東西。謝謝@hotkey! – mfulton26

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") 
    } 
} 
+0

我接受了另一個,因爲它不可能實現靜態。無論如何,感謝您的建議,+1 – elect