我有一個關于 Kotlin 和泛型的問題。我有一個編譯錯誤,我不明白。我在下面有一個非常簡單的例子。由于 Kotlin 資料類不能從其他資料類繼承,我已經用組合完成了很多我的類設計。這導致嵌套資料類。在某些時候,我使用泛型來切換我可以傳遞給物件的引數的型別。我需要修改這些資料類的值,因為它們是不可變的,所以我使用復制函式并將這些東西放入一個 Mutator 類中。
class Mutator<out P : Params>(val form: Form<P>) {
fun modifyName(name: String): Form<P> =
when (form.params) {
is DefaultParams -> form.copy(params = form.params.copy(name = name)) // compile error
is ExtendedParams -> form.copy(params = form.params.copy(name = name)) // compile error
else -> form // also.. why do I need that.. Params is sealed..
}
}
sealed interface Params
data class DefaultParams(val name: String) : Params
data class ExtendedParams(val name: String, val age: Int) : Params
data class Form<out P : Params>(val params: P)
fun main() {
val form: Form<DefaultParams> = Form(DefaultParams("John"))
val mutator: Mutator<DefaultParams> = Mutator(form)
val newForm: Form<DefaultParams> = mutator.modifyName("Joe")
val next_form: Form<ExtendedParams> = Form(ExtendedParams("John", 30))
val next_mutator: Mutator<ExtendedParams> = Mutator(next_form)
val next_newForm: Form<ExtendedParams> = next_mutator.modifyName("Joe")
}
我在 when 塊的前兩個分支上出錯。
型別不匹配。必需:P 找到:DefaultParams
型別不匹配。必需:P 找到:ExtendedParams
不應該Params是 的上限P,因此可以很好地找到DefaultParams或ExtendedParams?并且也Params應該被密封,但我需要一個 else 塊。
uj5u.com熱心網友回復:
我建議您在此處使用未經檢查的演員表:
is DefaultParams -> form.copy(params = form.params.copy(name = name) as P)
is ExtendedParams -> form.copy(params = form.params.copy(name = name) as P)
請注意,由于方法的語意,這種未經檢查的強制轉換非常安全copy。copy不會更改其接收器的運行時型別。因此,傳遞副本 并不form.params比傳遞原件更不安全form.params。并且編譯器允許你在form.params這里傳遞原件。編譯器不夠聰明,無法計算出因為form.params是特定的Params,所以也P必須是特定的Params。
該when陳述句并非詳盡無遺,因為型別form.params不是Params,而是P。(是的,編譯器有時很愚蠢)。根據規范,窮舉when運算式的條件是:
系結運算式屬于密封類或介面
請注意,它沒有說“系結到密封類或介面的型別引數”。因此,如果您只添加as Params,它將變得詳盡無遺:
fun modifyName(name: String): Form<P> =
(form.params as Params).let { formParams ->
when (formParams) {
is DefaultParams -> form.copy(params = formParams.copy(name = name) as P)
is ExtendedParams -> form.copy(params = formParams.copy(name = name) as P)
}
}
uj5u.com熱心網友回復:
我自己也不確定為什么會這樣,但我認為這與 P 是泛型有關。因為如果你像這樣重寫 Mutator,你可以省略 else 并且也沒有錯誤。
class Mutator(val form: Form<Params>) {
fun modifyName(name: String): Form<Params> =
when (form.params) {
is DefaultParams -> form.copy(params = form.params.copy(name = name)) // compile error
is ExtendedParams -> form.copy(params = form.params.copy(name = name)) // compile error
}
}
事實上,我認為對于您的用例,您不需要泛型,而這個解決方案可能正是您所需要的。
編輯:
如果你把它修改成這個
class Mutator<out P : Params>(val form: Form<P>) {
fun modifyName(name: String): Form<P> =
when (form.params) {
is DefaultParams -> form.copy(params = (form.params.copy(name = name) as P))
is ExtendedParams -> form.copy(params = (form.params.copy(name = name) as P))
else -> form
}
}
它應該像你想要的那樣作業。它將不再有編譯器錯誤。盡管這as P是一個未經檢查的演員表,但它會發出警告。但對我們來說,很容易看出這永遠不會成為問題。我猜編譯器不夠聰明。
uj5u.com熱心網友回復:
您必須將引數強制轉換為,P因為這是編譯器對該copy函式的期望。但是,這會給您一個 Unchecked cast 例外。您可以通過使用行內函式并將您的型別引數宣告為具體化來避免這種情況(請參閱有關未經檢查的強制轉換的Kotlin 檔案)。
inline fun<reified P: Params> Form<P>.modifyName(name: String): Form<P> =
when (this.params) {
is DefaultParams -> copy(params = params.copy(name = name) as P)
is ExtendedParams -> copy(params = params.copy(name = name) as P)
else -> this
}
sealed interface Params
data class DefaultParams(val name: String) : Params
data class ExtendedParams(val name: String, val age: Int) : Params
data class Form<out P : Params>(val params: P)
fun main() {
val form: Form<DefaultParams> = Form(DefaultParams("John"))
val newForm: Form<DefaultParams> = form.modifyName("Joe")
val nextForm: Form<ExtendedParams> = Form(ExtendedParams("John", 30))
val nextNewForm: Form<ExtendedParams> = nextForm.modifyName("Joe")
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/345170.html
下一篇:java.lang.ExceptionInInitializerError:在minecraftkotlin插件中為null
