I guess I have too much time on my hands, but here’s an implementation of asIntersection3
, albeit with a tiny caveat (playground):
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
@Suppress("BOUNDS_NOT_ALLOWED_IF_BOUNDED_BY_TYPE_PARAMETER")
@OptIn(ExperimentalContracts::class)
// R is automatically inferred to just be T1 & T2 & T3. I used the TypeWrapper trick so that then
// the callsite doesn't have to supply a type for R since you can't explicitly write out intersection types.
inline fun <reified T1, reified T2, reified T3, R> Any.asIntersection3(type: TypeWrapper3<T1, T2, T3>): R?
where R : T1, R : T2, R : T3
{
// You can add this if you really want to so that the receiver is from here on known to be
// of the intersection type T1, T2, and T3 so that one can just do `if(x.asIntersection3(blablabla) != null)`
// and use x itself after that.
contract {
returnsNotNull() implies (this@asIntersection3 is T1 &&
this@asIntersection3 is T2 &&
this@asIntersection3 is T3
)
}
// I tried to use takeIf here and it didn't work by the way; bummer!
return if(this is T1 &&
this is T2 &&
this is T3) this as R else null
}
interface Interface1 {
val i1: String
}
interface Interface2 {
val i2: Int
}
interface Interface3 {
val i3: Boolean
}
class Both : Interface1, Interface2, Interface3 {
override val i1: String = "one"
override val i2: Int = 2
override val i3: Boolean = true
}
fun anyType(): Any = Both() // We mask the type here just to add another layer of "Any" for fun.
fun main() {
val bar: Any = anyType()
bar.asIntersection3(type<Interface1, Interface2, Interface3>())?.let { baz ->
baz // Check the type here in Intellij with `ctrl+shift+p`
println(baz.i1)
println(baz.i2)
println(baz.i3)
}
}
sealed class TypeWrapper3<out T1, out T2, out T3> {
@PublishedApi internal object IMPL: TypeWrapper3<Nothing, Nothing, Nothing>()
}
inline fun <T1, T2, T3> type(): TypeWrapper3<T1, T2, T3> = TypeWrapper3.IMPL
As explained in the code, the callsite can’t really provide R, and so I had to go with a trick to carry that type information around. Idk how or why but I came up with that TypeWrapper trick when I was experimenting a bit with making DSLs nicer to use, and it just stuck with me ever since.