s3rius
06/17/2024, 10:18 AMLogger
interface which includes a tag
member. I have a BaseLogger
which provides a default implementation of Logger
. And I use that class via delegation in other classes: class OtherClass : Logger by BaseLogger()
. Now I'm running into a problem where the tag
isn't derived in the way I need it to.
Hard to explain- code is in 🧵s3rius
06/17/2024, 10:19 AMinterface Logger {
val tag: String?
fun log(): String? // would usually print to log
}
open class BaseLogger : Logger {
override val tag: String? = "base"
override fun log() = tag
}
class B : Logger by BaseLogger() { // delegation
override val tag = "b"
}
class C : BaseLogger() { // inheritance
override val tag = "c"
}
@Test
fun t() {
val b = B()
val c = C()
assertThat(b.log()).isEqualTo("b") // fail. actual=base!
assertThat(c.log()).isEqualTo("c") // ok
}
Here's an example, including a test-case.
I want to be able to override tag
in my classes and use the generalized log()
function to print message+tag. But as you can see, when I use delegation to implement Logger
as I do in class B
, the tag isn't b
but instead base
.
I understand the reason for it: delegation constructs the delegate first (which has tag="base"
and applies it to B
afterwards. But I'm looking for an elegant way around it.
I can't do something like class B : Logger by BaseLogger(tag)
either, because tag
is still undefined at this point.
I could do class B : Logger by BaseLogger("b")
but that ends up being pretty inconvenient due to the way the code is structured. (This example is simplified.)hho
06/17/2024, 11:43 AMNote, however, that members overridden in this way do not get called from the members of the delegate object, which can only access its own implementations of the interface membersSo yeah, inheritance is probably required here.
s3rius
06/17/2024, 12:11 PMclass B : Logger by BaseLogger() {
val log = createLogger(tag = "b") // uses BaseLogger to construct a logger
}
It's not great, but I can get it to work with DI and tests. 🤷