y
05/19/2025, 8:37 PMFoo
that is not implementable by something that implements Bar
?
a static check is preferred but I'll settle with some sort of automatic runtime checkephemient
05/19/2025, 8:40 PMsealed
you can't prevent additional implementations of Foo & Bar
elsewherey
05/19/2025, 8:41 PMsealed
you can't stop someone from also implementing Foo
for something that is Bar
, right?y
05/19/2025, 8:41 PMephemient
05/19/2025, 8:42 PMsealed interface Foo
and you know that none of the implementations of Foo
in your module are open
to additional implementations that might implement Bar
, I meany
05/19/2025, 8:44 PMy
05/19/2025, 8:44 PMFoo
and a version that takes Bar
, would fail to compile.Youssef Shoaib [MOD]
05/19/2025, 8:44 PMBar
, but with an incompatible return type, then seemingly it should be very hard (without using @Suppress
) to implement both:
interface Bar {
fun bar() {}
}
class Bad
interface Foo {
fun bar(): Bad = TODO()
}
//Object 'Illegal' must override 'bar' because it inherits multiple interface methods for it.
//'fun bar(): Bad' defined in '/Foo' clashes with 'fun bar(): Unit' defined in '/Bar': return types are incompatible.
object Illegal: Foo, Bar {
}
ephemient
05/19/2025, 8:45 PMfun negative(foo: Foo) = ...
@Deprecated(level = ERROR)
fun negative(bar: Bar)
but that wouldn't catch
fun otherFuntion(foo: Foo) = negative(foo)
class FooBar : Foo, Bar
otherFunction(FooBar())
Youssef Shoaib [MOD]
05/19/2025, 8:46 PMSuppress
and other things, and I can't figure it out)y
05/19/2025, 8:47 PMYoussef Shoaib [MOD]
05/19/2025, 8:48 PMAny
or Any?
. Investigating if it's possible right now. The point is just you need to have a return type that's incompatible. This should cover most interfaces that you'd like to do this with though!ephemient
05/19/2025, 8:49 PMsealed class FooMarker
sealed class BarMarker
interface Foo {
fun marker(): FooMarker = TODO()
}
interface Bar {
fun marker(): BarMarker = TODO()
}
but yeah, that should prevent coincidental implementations in Kotlin (and Java) sourcesephemient
05/19/2025, 8:50 PMy
05/19/2025, 8:50 PMephemient
05/19/2025, 8:50 PMYoussef Shoaib [MOD]
05/19/2025, 8:51 PMy
05/19/2025, 9:04 PMYoussef Shoaib [MOD]
05/19/2025, 9:05 PMDONT_OVERRIDE_THIS_FUNCTION
to really hammer it home to the user lol!y
05/19/2025, 9:05 PMFooMarker
and BarMarker
non-sealed
classes and then just doing fun marker(): FooMarker = FooMarker()
, fun marker(): BarMarker = BarMarker()
as the default impls?ephemient
05/19/2025, 9:06 PMFooMarker
etc. and strip out that codeephemient
05/19/2025, 9:07 PMy
05/19/2025, 9:07 PMYoussef Shoaib [MOD]
05/19/2025, 9:14 PMFooMarker
and BarMarker
classes. Maybe give them backticked names as well, with unicode characters inside, like
`bro's really trying to use this type 💀`
and maybe mark the method itself with @Deprecated(DeprecationLevel.HIDDEN)
(ooooo, and @JvmSynthetic
)ephemient
05/19/2025, 9:16 PM@JvmName
in interfaces but those should be possible. up to you to decide if they're overkill though :pYoussef Shoaib [MOD]
05/19/2025, 9:19 PMINAPPLICABLE_JVM_NAME
lol. It's maybe not the best idea, but it'll make it even harder for the user to override it correctly.ephemient
05/19/2025, 9:21 PM@Suppress("INAPPLICABLE_JVM_NAME")
is that they can lead to runtime `IncompatibleClassChangeError`s without having errors in the source codeephemient
05/19/2025, 9:21 PMSeri
05/20/2025, 2:50 PMSeri
05/20/2025, 2:52 PM@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.SOURCE)
annotation class ExcludeInterface(
val entity: KClass<*>
)
@ExcludeInterface(Vegetable::class)
interface Fruit
@ExcludeInterface(Fruit::class)
interface Vegetable
class Apple: Fruit
class Spinach: Vegetable
// Produces compile-time error via ksp symbol processor
class IllegalProduce: Fruit, Vegetable