Hmm might it be somehow possible to force the chil...
# announcements
d
Hmm might it be somehow possible to force the children of a sealed class to have a copy method?
Copy code
sealed class Level {
    abstract val exercise: Exercise
    abstract val finished: Boolean


    data class PresetLevel(
            override val exercise: Exercise,
            override val finished: Boolean) : Level()

    data class GeneratedLevel(
            override val exercise: Exercise,
            override val finished: Boolean) : Level()
}
Both children are data classes but since there is no "type" data class I can't say
level.copy(..)
even though both child classes have a copy method
👍 2
r
Can you implement
copy
in the sealed class and have it be final?
s
I’m still holding out hope for `data interface`s…. 🙂
d
It would have to be abstract instead of final, correct? Since I need to return a new instance of the data class on copy. I tried adding an abstract method
copy(..)
to the sealed class and I get an compile error that it clashes with the copy method generated by
data
😕
s
Copy code
data interface LevelIfc {
    val exercise: Excercise
}

sealed class Level: LevelIfc {
    override val exercise: Excercise
    abstract val finished: Boolean
    ...
}
Now
Level.copy(…)
would have been generated, but alas data-interface don’t exist…
r
Oh I see what you mean. I think you can do it with an extension function maybe?
Copy code
fun Level.mkCopy(): Level {
  when(this) {
    is PresetLevel -> ..
    is GeneratedLevel -> ..
  }
}
I guess it doesn't have to be an extension function, just give it a different name
d
If you have the concrete type yes, then copy is available, but it would be nice to do something with the
Level
type 😕
Copy code
override fun finishLevel(level: Level) {
        val finishedLevel = level.copy(
                finished = true,
                // other stuff 
        )

        when(finishedLevel) {
            is PresetLevel -> presetLevelDatabase.saveLevel(finishedLevel)
            is GeneratedLevel -> generatedLevelDatabase.saveLevel(finishedLevel)
        }
    }
This can be achieved, but I only could do it when I define my own abstract copy method in the sealed class
r
I'm not sure what you mean,
Level
is a sealed class, so all the concrete types that extend it are in the same file..
d
With when I have the concrete type I mean like it is in the
when
Then I have the type
PresetLevel
instead of just
Level
then the copy method of the data class would be available
r
hmm, I see what you mean. I'd consider a refactoring the design then, so that your data classes don't have business logic contained within their functions, and instead "Service" classes have the business logic (in your case a db persistence class) and that's the one that makes different decisions based on the type.
Copy code
Class LevelService(db: DbService) {
  fun finishLevel(level: Level) {
    when (level) {
      is PresetLevel -> db.savePresetLevel(level.copy(finished = true))
      is .. -> db.saveWhatever(level.copy(finished = true))
    }
  }
}
yes there's some repetition there
d
Sadly thats how it is right now. The
override fun finishLevel(level: Level)
method is in an repository and is responsible to bring the level in the finished state. The Level itself does not know anything, only its properties. The thing is: like you posted, I want to avoid the repetition
So having some king of
copy
method on the sealed class seems to be the only way - since the sealed class can't know that its subtypes are data classes 😕
r
maybe the repetition is unavoidable, but at least it's localized and backed by compiler validation that you didn't forget classes if new extentions to
Level
are created.
d
Might be better than to have a bit of repetition than implement awkward own
copy
methods in the end! Thank you for your replies!