2016-03-15 126 views
5

我想創建一個抽象類與泛型參數,它將有子類應調用方法而不必指定類型參數。 我有這個至今:Kotlin抽象類與泛型參數和使用類型參數的方法

abstract class AbstractClass<T : Any> @Autowired constructor(protected val delegate: MyService) { 
    inline fun <T: Any> myMethod(param: Any): T? { 
     return delegate.myMethod(param).`as`(T::class.java) 
    } 
} 

與實現

class TesterWork @Autowired constructor(delegate: MyService) : AbstractClass<Tester>(delegate) { 
} 

現在呼籲myMethod當我有指定類型參數:

testerWork.myMethod<Tester>("test") 

我想知道這將是可能的自動推斷類型參數? 我可以以某種方式重寫myMethod嗎?請注意,我需要在方法內有T::class.java

回答

8

您不能使用類的泛型參數作爲泛化泛型(獲取其T::class令牌),因爲在運行時泛型參數將被刪除:Kotlin遵循Java's type erasure practice,並且沒有爲類指定泛型。
(科特林具有reified generics只爲內聯函數)

鑑於這種情況,這是由你來傳遞和存儲Class<T>令牌,以便您可以使用它。

此外,myFunction在你的榜樣引入了泛型參數,這將是一個新的通用的,沒有連接到任何方式的類屬參數(命名都T只會增加混亂,認爲他們T1T2)。如果我理解正確,那麼你的意思就是這個類是泛型的。

也許你可以做什麼是聲明一個抽象val,將存儲類令牌和重寫功能,使得它使用存儲類令牌:

abstract class AbstractClass<T : Any> constructor(protected val delegate: MyService) { 
    protected abstract val classToken: Class<T> 

    fun myMethod(param: Any): T? { 
     return delegate.myMethod(param).`as`(classToken) 
    } 
} 

然後,從AbstractClass獲得需要重寫classToken

class TesterWork constructor(delegate: MyService) : AbstractClass<Tester>(delegate) { 
    override val classToken = Tester::class.java 
} 

之後,你將能夠呼籲TesterWork實例功能,而不指定泛型參數:

val service: MyService = ... 
val t: Tester? = TesterWork(service).myMethod("test") 
+0

感謝您的回答!我有點困惑,爲什麼不能類泛型參數連接到方法泛型參數。當子類被創建時,我們知道泛型類型,爲什麼我們不能在抽象類中使用它作爲「T類型」?爲什麼我們必須重新指定類型? – NikolaB

+0

@NikolaB,當你爲**類和它的方法添加一個泛型參數時,那些將是不相互連接的不同參數,這就是我的意思。否則,你肯定可以在函數中使用該類的泛型參數。至於抽象類,JVM上的泛型以不同的方式工作:對於每個派生類,方法代碼不會被複制,因此在派生類中指定具體類型代替泛型參數將無濟於事:方法的代碼是仍然在基類中,然後泛型將從其中刪除。 – hotkey

+0

@NikolaB,可能是閱讀* https://en.wikipedia.org/wiki/Generics_in_Java中的type erasure *部分會使事情更加清晰。 – hotkey

3

在這裏玩有幾個問題。首先,爲了從泛型類型參數中檢索類對象,需要對其進行具體化。爲此,您需要聲明<reified T: Any>

其次,你聲明泛型類型參數兩次。一旦進入類別聲明abstract class AbstractClass<T : Any>並且一旦進入方法聲明inline fun <T: Any> myMethod。那些T s是不一樣的。從理論上講,你可以省略方法簽名中的一個,但這不起作用,因爲只有內聯方法和類沒有關係。有關可能的解決方案,請參閱@ hotkey的答案。

1

你可以給抽象類鑄件拉姆達:

abstract class AbstractClass<T : Any>(val delegate: MyService, val castFn: (Any) -> T) { 
    fun myMethod(param: Any): T? { 
     return castFn(delegate.myMethod(param)) 
    } 
} 

class TesterWork(delegate: MyService) : AbstractClass<Tester>(delegate, {it as Tester}) { 

} 

也許你可以也使類非抽象的,給它一個接口,讓內聯函數來創建它:

interface Inter<T> { 
    fun myMethod(param: Any): T 
} 

class NotAbstractClass<T>(val delegate: MyService, val castFn: (Any) -> T) : Inter<T> { 
    override fun myMethod(param: Any): T { 
     return castFn(delegate.myMethod(param)) 
    } 
} 

inline fun <reified T> makeClass(delegate: MyService): Inter<T> { 
    return NotAbstractClass<T>(delegate, { it as T }) 
} 

然後你可以這樣委託:

class TesterWork(delegate: MyService) : Inter<Tester> by makeClass(delegate) 

val service: MyService = MyService() 
val t: Tester? = TesterWork(service).myMethod("test")