Let's say I have two interfaces ```interface FooA ...
# library-development
c
Let's say I have two interfaces
Copy code
interface FooA {
    fun foo(name: String, value: Int)
}

interface FooB {
    fun foo(value: Int)
}
The two interfaces represent two different ways of interacting with a single entity. In some contexts, the library provides an instance of the first interface, and in some contexts, the library provides an instance of the second interface. However, since they are both used to interact with a single entity, it's more convenient for the library author to implement a single instance that can be viewed as both:
Copy code
private class FooImpl : FooA, FooB {
    override fun foo(name: String, value: Int) {}
    override fun foo(value: Int) {}
}
So far, everything is good I think. I think however there is a risk that a user would use a cast to change their access context even if it's not allowed.
Copy code
withFooA {      // provided by the library
    it as FooB  // will succeed because FooImpl does implement FooB
    it.foo(4)   // illegal call to FooB.foo in the context of FooA, but will compile and run
}
Should this be considered a risk? Should I split the implementations of the two interfaces to stop this usage? Or should I consider that this is bad code anyway and I should ignore the fact that users could write it?
e
it's easy to create wrappers by delegation,
Copy code
val foo = FooImpl()
val fooA = object : FooA by foo
val fooB = object : FooB by foo
c
Ah, you're right. And since all implementations are private anyway, this is a very low-cost way to avoid the issue.
e
I'd usually consider it better to have separate
FooAImpl
and
FooBImpl
classes though. if you really need to hold them together, you can do so with
Copy code
internal class FooImpl(val fooA: FooA, val fooB: FooB) : FooA by fooA, FooB by fooB
and then pull out the individual
fooA
,
fooB
when passing to users outside your library
c
Well I don't really need them together, it's just that all functions from
FooB
can be trivially implemented from the function of the same name in
FooA
, so it's more convenient for me to co-locate them
But yeah I'll probably use interface delegation to create wrapper types that can't be downcasted
j
Personally I don't consider this as a risk and never complicate my code to defend against it. I consider these casts as bad behavior on the consumer part and if they do this they should expect stuff to break.
same 4