Data class inheritance

Hello everyone, I want to suggest a new feature for Kotlin. I haven’t found category for this topic and that’s why I’m writting there. Now I am developing API and working with framework that makes the api typed. For example my simple request:
get<GetParamsType, GetResponseType> {...}, where GetParamsType is data class with params and GetResponseType is response type

So, there are cases where I need use parameters repeatedly and library does not provide anything for this case. I thought that it will be nice feature if I could add up data classes. I don’t know about syntax, but I can share my vision:

@Path("/{id}")
data class IdParams(
    @PathParam
    val id: Long
)
// I can use that data class to get public users list e.g.
data class GetParams(
    @QueryParam
    val offset: Long,
    @QueryParam
    val limit: Long,
    @QueryParam
    val filter: String
)

// That one I can use to get user's photos
// I have not passed params to parents' constructor, but the class have inherited it to self constructor
data class GetWithIdParams(
     ... some other arguments maybe ...
) : GetParams, IdParams

// Another vision of the syntax
// There can be only ability to "sum" data classes, but not inherite
data class GetWithIdParams = IdParams + GetParams

That’s all I want to suggest. Btw if there is already way to do something same, write it

3 Likes

Yeah, that’s really what I’m missing. I liked the “sum data class types” syntax. It’d be great.

3 Likes

This explains why Kotlin cannot support data-class inheritance.

You cannot extend a Data class or make it abstract. So probably, you won’t use them in a core domain model.
This limitation is not Kotlin’s fault. There is no way to generate the correct value-based equals() without violating the Liskov Principle. That’s why Kotlin doesn’t allow inheritance for Data classes.

1 Like

That is not actually what I’m talking about. In fact it is not inheritance. I can make same via annotation processing, I’m talking about creating constructor for data class that will sum arguments of it’s “parent” classes.
I will definitely create a library when I will have free time. The syntax may be something like this:

@InheritData(OtherDataClass::class)
data class MyDataClass(...)

But it could be nice language feature, isn’t it ?

This sounds like it should be implemented in terms of delegation. I’m envisioning the following:

data class Foo(x: A, y: B)
data class Bar(z: C)
data class FooBar(foo: Foo, bar: Bar)
    as Foo by foo, as Bar by bar // generates delegating accessors

Idea about “sum” classes is very abstract, I don’t know how to implement it, but it just looks great. I need to use the class as 1D array, can I ?

Delegation in the kotlin style still means that FooBar inherits from Foo and Bar. Do you mean composition? Then you can generate getters and setters for the properties of the component types.

Also this is something that could easily be done with annotation processing. Given an input like this

data class Foo(val x: A, var y: B)
data class Bar(val z: C)
data class FooBar(@GenerateAccessors foo: Foo, @GenerateAccessors bar: Bar)

you can use kapt to generate the following extension properties

val FooBar.x get() = foo.x
val FooBar.y
   get() = foo.y
   set(value) { foo.y = value}
val FooBar.z get() = bar.z

This would also work for non data classes which means that it can work with java classes. The only problem is that it might be a bit harder to find the right getters and setter if you don’t rely on kotlin meta data, but it should still be possible.

Yes, that’s want I want to do, but, as I said before it can be nice language feature

If this is what you want, I don’t think waiting for this to be introduced as a language feature is the right aproach. Language features take a lot of time. It would require a KEEP, then the KEEP needs to be accepted by the kotlin team and then it will be implemented. I don’t see this happening in the near future and it will probably take a year or two at a minimum.
A faster way would be to implement this as a small library. This can probably be done in a day, maybe 2 or 3 tops if you are new to kapt. If you need this for something else than Kotlin JVM (MPP, JS and native aren’t supported by kapt) you can look into creating a compiler plugin. arrow meta should help with that, but it will be a bit more complicated. Arrow meta is still quite new and I don’t have any real experience with it.
Anyways having a working prototype will help a lot with getting the KEEP accepted and it has the benefit that you can use it years before this will become a language feature.

I know about time, and for my case, I will write library that using kapt, but I just also written there about it, beacause data class A = B + C looks great and may have many usages

@y9san9, why composition isn’t appropriate for your case?

You can create data class A(val b: B, val c: C)

I thank previously about inheritance in data classes, however for now I found, that for long-term projects, event for small types composition is better than inheritance.

Moreover, you have final classes for JVM, which is faster and ease to optimize. In future Kotlin (I hope) will reuse Java Record ideas for new byte code (because, as I think JVM will generate faster code for Record-like types).

So finally (just my opinion) disabled data classes inheritance covers the most of cases and make code ease. For any other tricky case (with custom hashCode, however standard equals, etc.) everyone is able to crease own dto.

Because library I use works only with data classes contain params with annotations. And in fact it should be 1D data class. For my other cases I use composition

Because library I use works only with data classes contain params with annotations.

I think I understand. As possible example, it can be external dto which has common block of code (for example - self link for output json dtos).

Did you consider interface for this?

E.g. you can have:

interface B {
     @JsonParameter("double_value")
     val field1: Double
}

interface C {
     @JsonParameter("int_value")
     val field2: Int
}

data class A(
      val override field1: Double,
      val override field2: Int,
     @JsonParameter("char_value")
      val field3: Char  
): B, C

?