orca
06/07/2024, 7:55 AMabstract class Thing {
fun someFunctionWeWant(): String {
// ...
return "hello!"
}
}
interface IThingAdjacentInterface : Thing {
val someProp: String
fun doSomething() {
val data = someFunctionWeWant()
// ...
}
}
class Something : Thing(), IThingAdjacentInterface {
override val someProp = "hi!"
}
fun processSomeStuff() {
val items: List<IThingAdjacentInterface> = listOf(Something())
// ...
items.forEach {
it.doSomething()
someMoreStuff(it.someProp)
}
}
Funnily enough Intellij actually thinks this is absolutely okay, except that it highlights interfaces can't inherit from classes. Otherwise it's happy with this and completes methods.
I'm currently feigning this with context receivers:
interface IThingAdjacentInterface {
val someProp: String
context(Thing)
fun doSomething() {
val data = someFunctionWeWant()
// ...
}
}
// ...
fun processSomeStuff() {
val items: List<Thing> = listOf(Something())
// ...
items
.forEach {
if (it is IThingAdjacentInterface) {
// `run` means its context receiver is... itself, compiler is happy with this.
it.run { doSomething() }
}
}
}
I got my hopes up for a moment when I noticed Intellij doesn't actually mind if you do this:
context(Thing)
interface IThingAdjacentInterface
...but I think that's a bug since it doesn't actually cause anything nor prevent you applying the interface to other classes, nor provide its context properties from Thing
.
I had a look into Kotlin's history and other questions about this and found that apparently Traits used to have this functionality via allowing them to inherit from classes, but I think that's a bit different to this.
The final piece of the puzzle here I'd point to as well is that I, in the code I'm working with, do not have the option to simply define an interface for the functionality of Thing
. That'd resolve the whole issue, but Thing
already exists and I cannot modify its definition to add an interface implementation there.Sam
06/07/2024, 7:59 AMinterface Thing {
fun someFunctionWeWant(): String
}
class ConcreteThing : Thing {
override fun someFunctionWeWant(): String = "hello!"
}
interface IThingAdjacentInterface : Thing {
val someProp: String
fun doSomething() {
val data = someFunctionWeWant()
// ...
}
}
class Something : IThingAdjacentInterface, Thing by ConcreteThing() {
override val someProp = "hi!"
}
Sam
06/07/2024, 8:01 AMSam
06/07/2024, 8:03 AMIThingAdjacentInterface
an abstract class instead of an interface? That would make the code you shared work as-is, but I assume the real-world case is more tricky.Youssef Shoaib [MOD]
06/07/2024, 10:09 AMinterface IThingAdjacentInterface {
val asThing: Thing
...
fun doSomething() {
val data = asThing.someFunctionWeWant()
}
}