Feature Request: Safe Generics

What?
So, i’ve got this idea, where the generics on classes could be safe to use as if they were inline reified, or technically, the type is not erased.

Why?
The main concern for this is for more abstract use cases. Especially on Clean architecture and DDD. Redundancy in code is something we usually prevent from doing as much as possible.

Possible Usage
Consider this example:

@Serializable
class MySerializableClass1

@Serializable
class MySerializableClass2

interface ToJson<T> {
    fun toJson(obj: T): String
}

class MySerializableClass1ToJson : ToJson<MySerializableClass1> {
    override fun toJson(obj: MySerializableClass1): String {
        return Json.encodeToString(obj)
    }
}

class MySerializableClass2ToJson : ToJson<MySerializableClass2> {
    override fun toJson(obj: MySerializableClass2): String {
        return Json.encodeToString(obj)
    }
}

Of course, you could always just simplify this to this to minimize redundancy in code:

@Serializable
class MySerializableClass1

@Serializable
class MySerializableClass2

class ToJson<T> {
    fun toJson(obj: T): String {
        if (obj is MySerializableClass1) {
            return Json.encodeToString(obj)
        } else if (obj is MySerializableClass2) {
            return Json.encodeToString(obj)
        }
        throw IllegalArgumentException("Unknown type")
    }
}

But, this would mean that ToJson is not extendable, and we would have to add a new class if we were to use GSON instead of kotlinx.serialization.

So inorder to turn it into a contract again, we should create two generic classes. Just to make it more interesting, we are making two implementations, for kotlinx.serialization and GSON.
GSON:

class GsonToJson<T> : ToJson<T> {
    override fun toJson(obj: T): String {
        return Gson().toJson(obj)
    }
}

kotlinx.serialization:

class KotlinxToJson<T> : ToJson<T> {
    override fun <T> toJson(obj: T): String {
        return Json.encodeToString(obj)
    }
}

The problem with this now is Json.encodeToString is an inline function with a generic type. This means, it requires toJson to be inline too. And there’s no way to do that because we can’t inline functions on abstract classes.

How?
What i’m thinking is this is a compiler feature for kotlin where the compiler looks for usages of a class/interface that has generic parameters, then generates classes for these classes/interfaces such that they could be marked as safe to use and there won’t be any type erasure.

You just brought up probably one of the most controversial design choices in Java, which was discussed in numerous conversations for last 20 years and you say to simply fix it :wink: See:

We were solving such cases for years, usually by passing Class/Type explicitly. Kotlin made it a little easier with its reified generics in inline functions. There were some discussions about reified classes as well (type is captured at initialization time), but I don’t know if there is any real work happening for this.

One thing I don’t like right now is that there is no way to call an inline reified function by passing the KClass/KType. This makes it sometimes impossible to call a function, even if there are no technical limitations that would justify this. In the case of kotlinx serialization, I believe it has an experimental feature to provide types as KClass/KType. This way you can solve your problem with just a single class.

2 Likes

I’m not saying that its easy and I’m especially not saying fix it. I just had an idea in mind and i thought a feature request for this would be good. I didn’t mean to offend you if that’s how it looks like what i sounded like.

Thanks for letting me know about these things. I didn’t know about these things when i was writing my feature request.

I just mean that the idea of generating multiple implementations is a well-known one and Java designers intentionally decided to not go this way 20 years ago. There are still discussions if that was a good decision or not - there are pros and cons. I bet Kotlin designers had the same discussion and either they got to similar conclusions as Java designers years ago, or they just didn’t see a way to switch to another generics implementation in Kotlin without making Java interop painful. And so we are where we are :slight_smile:

2 Likes