How to write generic functions in Kotlin interfaces's implementations - kotlin

I am trying to implement a generic HttpClient like this one:
interface HttpClient {
fun <T: Any> get(url: String): T?
}
implemented by a class like this:
class HttpClientImpl #Inject constructor(...) : HttpClient {
override fun <T : Any> get(url: String): T? = execute(url)
private inline fun <reified T: Any> execute(url: String): T? {
val request = Request.Builder().url(url).get().build()
client.newCall(request).execute().use {
return it.body?.parseBodySuccess()
}
}
private inline fun <reified T: Any> ResponseBody?.parseBody(): T? {
val type = objectMapper.typeFactory.constructType(T::class.java)
return this?.let { objectMapper.readValue(it.string(), type) }
}
}
Now, I would like to be able to call such GET method in this way:
data class MyEntity(...)
class MyService #Inject constructor(private val client: HttpClient) {
fun performGet(url: String): MyEntity? = client.get<MyEntity>(url)
}
However this is not allowed and the compiler throws an error referring to the line of code
override fun <T : Any> get(endpoint: String): T? = execute(endpoint)
flagging that : Cannot use 'T' as reified type parameter. Use a class instead.
I have been trying to re-write the line as
override inline fun <reified T : Any> get(endpoint: String): T? = execute(endpoint)
however, despite having to make the other two inline functions "non private" the compiler still won't compile because in this last way of writing the overriding function, it says:
Override by a function with reified type parameter
How can I achieve such generic function?

I ended up doing something like this:
interface HttpClient {
fun <T: Any> get(url: String, type: Class<T>): T?
}
implemented as:
class HttpClientImpl #Inject constructor(...) : HttpClient {
override fun <T : Any> get(url: String, type: Class<T>): T? = execute(url, type)
private fun <T: Any> execute(url: String, type: Class<T>): T? {
val request = Request.Builder().url(url).get().build()
client.newCall(request).execute().use {
return it.body?.parseBody(type)
}
}
private fun <T: Any> ResponseBody?.parseBody(type: Class<T>): T? {
val dataType = objectMapper.typeFactory.constructType(type)
return this?.let { objectMapper.readValue(it.string(), dataType) }
}
}
that I can call in this way:
data class MyEntity(...)
class MyService #Inject constructor(private val client: HttpClient) {
fun performGet(url: String): MyEntity? = client.get(url, MyEntity::class.java)
}
I would have preferred to pass the Type directly as an actual type like
client.get<MyEntity>(url)
rather than passing the Class as a parameter, however, just for now it works...
If anyone can suggest a better way of doing this, please let me know.
Updated
As suggested by Pawel, I have created an extra inline extension function to the HttpClient interface
inline fun <reified T:Any> HttpClient.get (url: String) = get(url, T::class.java)
And I'm now able to call the function the way I wanted.

Related

Kotlin - Override a function with Generic return type

I have an interface. I want to make the return type of one of the function in that interface as generic type. Depend on how it is going to override, the return type will determine. When I try below code, I get errors Conflicting overloads: . I am new to Kotlin.
interface Loader<T> {
fun <T> doSomething(inputParams: Map<String, String> = emptyMap()): T?
fun cancelSomething(inputParams: Map<String, String> = emptyMap())
}
class LoaderHandler<MutableMap<String, String>> (private val foo: Foo) : Loader<MutableMap<String, String>> {
override fun doSomething(inputParams: Map<String, String>): MutableMap<String, String>? {
// some code goes here.
return mapOf("x" to "y")
}
override fun cancelSomething (inputParams: Map<String, String>) {
println("cancelSomething")
}
How can I implement the doSomething(...) function with return type of Map.
Delete <T> in
fun <T> doSomething(inputParams: Map<String, String> = emptyMap()): T?
It is not doing what you think it is.
Additionally,
class LoaderHandler<MutableMap<String, String>> (private val foo: Foo) : Loader<MutableMap<String, String>> {
should be
class LoaderHandler(private val foo: Foo) : Loader<MutableMap<String, String>> {

extension function on List for elements compliant to multiple interfaces

The following code creates an extension function for List that contain elements that are implementing the InterfaceCustomFirebaseData.
fun List<InterfaceCustomFirebaseData>.toFirebaseDate(): MutableMap<String, Any> {
val firebaseData: MutableMap<String, Any> = mutableMapOf()
this.forEach { firebaseData[it.id] = it.toFirebaseData() }
return firebaseData
}
But I need the List elements to be compliant to 2 interfaces (InterfaceCustomFirebaseData and also InterfaceIdentifiable), but this does not work:
fun List<InterfaceCustomFirebaseData, InterfaceIdentifiable>.toFirebaseDate(): MutableMap<String, Any> { ... }
The compiler gives error "One type argument expected for interface List"
How should this be written in Kotlin?
Found the answer myself:
fun <T> List<T>.toFirebaseDate(): MutableMap<String, Any> where T : InterfaceCustomFirebaseData, T : InterfaceIdentifiable {
val firebaseData: MutableMap<String, Any> = mutableMapOf()
this.forEach { firebaseData[it.id] = it.toFirebaseData() }
return firebaseData
}

kotlin interfaces, sealed classes and generic types usage (using child classes of generic types in interface functions)

I have this sealed class
sealed class S3FileUploaderRequest {
abstract val s3Configuration: S3Configuration
data class S3UploadRequest(
val uploadData: S3UploadData,
override val s3Configuration: S3Configuration
) : S3FileUploaderRequest()
data class S3DeleteRequest(
val path: String,
override val s3Configuration: S3Configuration
) : S3FileUploaderRequest()
data class S3MultiUploadRequest(
val multiUploadData: List<S3UploadData>,
override val s3Configuration: S3Configuration
) : S3FileUploaderRequest()
data class S3MultiDeleteRequest(
val paths: List<String>,
override val s3Configuration: S3Configuration
) : S3FileUploaderRequest()
}
And this interface:
interface FileUploader<T, S> {
suspend fun multiUploadFile(request: T): S
suspend fun multiDeleteFile(request: T): List<S>
suspend fun uploadFile(request: T): S
suspend fun deleteFile(request: T): S
}
I would like to use the child classes of data class S3FileUploaderRequest es entry parameter of interface functions, somethig like this:
interface FileUploader<T, S> {
suspend fun multiUploadFile(request: out T): out S
suspend fun multiDeleteFile(request: out T): List<S>
suspend fun uploadFile(request: out T): out S
suspend fun deleteFile(request: out T): out S
}
Is possible to do this? I feel like kotlin should be able to do this but don't find the proper syntax for it.
Update:
I would like to implement the interface this way:
#Singleton
class S3FileUploader() : FileUploader<S3FileUploaderRequest, S3FileUploaderResponse> {
override suspend fun uploadFile(request: S3UploadRequest): S3UploadResponse {
//DO STUFF
}
override suspend fun deleteFile(request: S3DeleteRequest): S3DeleteResponse {
//DO STUFF
}
override suspend fun multiUploadFile(request: S3MultiUploadRequest): S3MultiUploadResponse {
//DO STUFF
}
override suspend fun multiDeleteFile(request: S3MultiDeleteRequest): List<S3DeleteResponse> {
//DO STUFF
}
}

Deserialize generic object using Kotlin Serialization

I am trying to replace Gson library by kotlin serialization to handle JSON serialization/deserialization.
I am facing some issues to deserialize generic objects I have setup a simple example of what I am trying to achieve:
#Serializable
data class ContentMessageDto<T>(
val method: String,
val content: T
)
#Serializable
private data class DummyObjectNonNullProperties(
val value: Int,
#SerialName("aaa") val someProp: String,
val bbb: Boolean,
val ccc: Double
)
interface MyParser {
fun <T> parseContentMessage(
json: String
): ContentMessageDto<T>
}
class MyParserImpl(private val jsonSerialization: Json) : MyParser {
override fun <T> parseContentMessage(json: String): ContentMessageDto<T> {
return jsonSerialization.decodeFromString<ContentMessageDto<T>>(json)
}
}
fun main() {
println("start processing...")
val jsonToParse = """
{
"method":"get",
"content":{
"value":345,
"aaa": "some string",
"bbb": true,
"ccc": 23.4
}
}""".trimIndent()
val parser:MyParser = MyParserImpl(Json)
val result = parser.parseContentMessage<DummyObjectNonNullProperties>(jsonToParse)
println("result -> $result")
}
But when I run the main method, I get the following error:
Exception in thread "main" java.lang.IllegalStateException: Only KClass supported as classifier, got T
at kotlinx.serialization.internal.Platform_commonKt.kclass(Platform.common.kt:102)
at kotlinx.serialization.SerializersKt__SerializersKt.serializer(Serializers.kt:52)
at kotlinx.serialization.SerializersKt.serializer(Unknown Source)
at kotlinx.serialization.SerializersKt__SerializersKt.builtinSerializerOrNull$SerializersKt__SerializersKt(Serializers.kt:79)
at kotlinx.serialization.SerializersKt__SerializersKt.serializerByKTypeImpl$SerializersKt__SerializersKt(Serializers.kt:69)
at kotlinx.serialization.SerializersKt__SerializersKt.serializer(Serializers.kt:54)
at kotlinx.serialization.SerializersKt.serializer(Unknown Source)
But I am not sure why. Can someone provide me an explanation and if possible some tips on how I can implement this?
It would have worked if you've simply done:
val result = Json.decodeFromString<ContentMessageDto<DummyObjectNonNullProperties>>(jsonToParse)
But with all this wrapping, type information about T was lost. The problem is that you can't simply use reified generics here, cause inline functions can't be non-final.
Possible workarounds:
Define parseContentMessage as extension function so that it could have inline modifier (and T could be reified):
interface MyParser {
val jsonSerialization: Json
}
inline fun<reified T> MyParser.parseContentMessage(json: String): ContentMessageDto<T> {
return jsonSerialization.decodeFromString(json)
}
class MyParserImpl(override val jsonSerialization: Json) : MyParser
//Usage will be the same
Manually pass serializer for T into parseContentMessage:
interface MyParser {
fun <T> parseContentMessage(json: String, contentSerializer: KSerializer<T>): ContentMessageDto<T>
}
class MyParserImpl(private val jsonSerialization: Json) : MyParser {
override fun <T> parseContentMessage(json: String, contentSerializer: KSerializer<T>): ContentMessageDto<T> {
return jsonSerialization.decodeFromString(ContentMessageDto.serializer(contentSerializer), json)
}
}
//Usage:
val result = parser.parseContentMessage(jsonToParse, DummyObjectNonNullProperties.serializer())

How to return T in Kotlin?

I want to return T in the function.
I have an interface class IRepository.kt
interface IRepository
{
fun <T>Save(model:T)
fun <T>Delete(model:T)
fun <T>Get(id:Long):T
}
I want to implement in Repolmpl.kt
class Repolmpl:IRepository
{
override fun <T>Delete(model:T)
{
println("$model : Save}")
}
override fun <T>Get(id:Long):T
{
return T //ERROR here I want to return T...
}
override fun <T> Save(model: T)
{
println("$model : Delete")
}
}
I saw some similar questions online but I just can't find the right solution.
A generic type T is basically just a template. You cannot return it but have to replace it with an actual type first. Make the interface itself generic, not its methods. When implementing, specify T:
interface IRepository<T> {
fun save(model: T)
fun delete(model: T)
fun get(id: Long): T
}
class Repolmpl: IRepository<String>
{
override fun delete(model: String) {}
override fun get(id: Long): String {}
override fun save(model: String) {}
}
You cannot just return T. T is type here, and it is like return String.
You have to return instance of T. So, sth like:
class Repo {
val data = mapOf<Long, Any>()
// ...
fun <T> get(id: Long): T {
return data[id] as T // Get data from somewhere and then cast it to expected type
}
}