我想要一個父介面/抽象類上的方法,它使用泛型方法來傳遞實作類的類。
interface Domain {
fun toJSON(): String { return Json.encodeToString(this) }
}
@Serializable
class User: Domain {
val a: Int
}
這不起作用,因為Json.encodeToString不知道“this”的類別。
@Serializable似乎實作了,KSerializer所以理論上我可以要求域從它派生,但該介面是模板化的。并且標記實作類@Serializable似乎KSerializer直到編譯時才實作,因此會產生錯誤。
我如何實作這個toJSON()方法或告訴域它的實作者必須是@Serializable/ KSerializer?
我也試過:
interface Domain<T> {
fun toJSON(): String { return Json.encodeToString(this) }
}
@Serializable
class User: Domain<User> {
val a: Int
}
但這導致:
kotlin.IllegalStateException: Only KClass supported as classifier, got T
所有這一切的另一個復雜因素是我試圖在 KMM 中做到這一點。
uj5u.com熱心網友回復:
Json.encodeToString使用具體化的泛型來訪問引數的類。這意味著它使用引數的宣告型別(在編譯時已知),這里是Domain.
最簡單的方法是使用您自己的通用具體化擴展函式來更精確地捕獲接收器的宣告類:
interface Domain
inline fun <reified T : Domain> T.toJSON(): String = Json.encodeToString(this)
@Serializable
data class User(
val name: String,
val age: Int,
): Domain
fun main() {
val user = User("Bob", 35)
println(user.toJSON())
}
但是請注意,這遇到了同樣的問題:如果您嘗試在使用Domaintype宣告的變數上使用此擴展,它仍然不會嘗試訪問運行時型別:
val user: Domain = User("Bob", 35)
println(user.toJSON()) // still a problem here
如果您想實際使用動態型別,則可以像他的回答中提到的@broot 那樣動態訪問序列化程式。
uj5u.com熱心網友回復:
@Serializable 似乎實作了 KSerializer
這不是真的。@Serializable是一個注解,指示注解處理器生成(除其他外)一個serializer()方法(用于伴隨物件),該方法回傳KSerializer此類的實體。它不會為類本身添加新介面,因此您無法告訴型別系統它應該檢查什么。
如果找不到序列化程式,您將收到運行時錯誤:
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
class HasNoSerializableAnnotation
fun main() {
// Compiles fine, but will throw runtime exception
// kotlinx.serialization.SerializationException: Serializer for class 'HasNoSerializableAnnotation' is not found.
Json.encodeToString(HasNoSerializableAnnotation())
}
這是故意的,因為可以在沒有插件生成的序列化程式的情況下序列化類的實體(即使您沒有將自定義序列化程式encodeToXXX作為另一個引數手動傳遞到方法中 - 通過在 中傳遞序列化程式(作為背景關系)serializersModule)。
我如何實作這個 toJSON() 方法或告訴域它的實作者必須是@Serializable
沒有辦法做到這一點。您可以使用此 API,但它容易出現運行時錯誤:
interface Domain
inline fun <reified T : Domain> T.toJSON() = Json.encodeToString(this)
我如何實作這個 toJSON() 方法或告訴域它的實作者必須是 KSerializer
實際上,您KSerializer在這里不需要整個- 只需要它的序列化部分:
interface Domain
inline fun <reified T : Domain> T.toJSON(serializer: SerializationStrategy<T>) =
Json.encodeToString(serializer,this)
uj5u.com熱心網友回復:
它不起作用,因為encodeToString()在編譯時決議型別,而不是在運行時(它使用具體化型別)。
也許有更好的方法,但您可以通過手動獲取序列化程式,在運行時決議型別來實作:
fun toJSON(): String {
val serializer = Json.serializersModule.serializer(this::class.createType())
return Json.encodeToString(serializer, this)
}
請注意,這僅適用于 JVM。如果您需要在 KMM 中執行此操作,請注意多平臺反射很復雜和/或需要一些時間才能成熟。kotlinx.serialization提供了一種方法來做到這一點,但有關于平臺之間可能存在不一致行為的警告:
@OptIn(InternalSerializationApi::class)
fun toJSON(): String {
@Suppress("UNCHECKED_CAST")
val serializer = this::class.serializer() as KSerializer<Any?>
return Json.encodeToString(serializer, this)
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/311143.html
上一篇:是否可以在Kotlin中為List提供泛型型別約束?
下一篇:帶有泛型的Scala工廠模式
