17

I'm struggling with data classes and polymorphism. I want to benefit from the immutability, but still be able to update my state. For this, I'm hoping to be able to use the copy function.

Let's give an example. I have this class hierarchy:

interface Aging {
  val age : Int
}

data class Cheese (
  override val age : Int
  // more Cheese specific properties
) : Aging

data class Wine (
  override val age : Int,
  val grape : String
  // more Wine specific properties
) : Aging   

And now I want to be able to do something like this (but this does not work):

class RipeningProcess(){
  fun ripen(products : List<Aging>) =
    // Not possibe, no copy function on Aging
    products.map { it.copy(age = it.age + 1) } 
}

How can I create updated copies in a polymorphic way?

I've tried to give the interface a copy function, but if the subtypes have additional properties they don't override the copy function.
It's frustrating since I know the subtypes have that property, but I cannot utilize that knowledge in the interface.

Stim
  • 1,455
  • 14
  • 28

1 Answers1

12

[OP:] The best I've come up with is indeed to declare the copy function in the interface:

interface Aging {
  val age : Int
  fun copy(age : Int) : Aging
}

This works out of the box for data class subtypes without additional properties (i.e. Cheese from the question). For data class subtypes with additional properties you need to declare it explicitly, since the generated copy function does not override the one from the interface.

The subtype with it's age-copy implementation looks like:

data class Wine(
  override val age : Int,
  val grape : String
) : Aging {

  // Different parameter name, to avoid conflict with generated copy method
  override fun copy(_age: Int) = copy(age = _age)
}

Hoping for better solutions (or a Kotlin improvement ;) ).

Edit: updated to follow Ghedeons suggestion.

Stim
  • 1,455
  • 14
  • 28
  • 2
    Another way to trick it into choosing the right method would be to have a different name for copy param. Ex: `override fun copy(_age: Int) = copy(age = _age)`. – Ghedeon Sep 06 '17 at 16:06