y
04/29/2024, 1:55 PMinline fun <reified T> Any.tryAs(): T? = this as? T
if I inline this function and use a type that is isn't a subtype of this
, I get a compiler warning UnsafeCast: this cast can never succeed
. is there some way of getting tryAs()
to have the same safeguard? maybe with kotlin.contracts
?ephemient
04/29/2024, 2:09 PMfilterIsInstance
extension has the same problem of having no warningy
04/29/2024, 2:10 PMy
04/29/2024, 2:11 PMSam
04/29/2024, 2:15 PMRob Elliot
05/01/2024, 1:04 PMinline fun <T, reified T2 : T> T.tryAs(): T2? = this as? T2
meet the safety needs?
Unfortunately it's ugly:
interface Foo
interface Bar : Foo
val foo: Foo = TODO()
val maybeBar: Bar? = foo.tryAs<Foo, Bar>()
I think there's a YouTrack around being able to infer some generic types while explicitly stating others which would stop this requiring <Foo, Bar>
when all that's logically needed is <Bar>
.Sam
05/01/2024, 1:17 PMI think there's a YouTrack around being able to infer some generic types while explicitly stating others which would stop this requiringThis is actually implemented already; you just need to use an underscore. So you can write `<_, Bar>`—but unfortunately that doesn't really work out. The compiler will actually infer the common supertype, which can just bewhen all that's logically needed is<Foo, Bar>
.<Bar>
Any
. To match the behaviour of the compiler warning I think you'd need something like the secret @Exact
annotation.Rob Elliot
05/01/2024, 1:25 PMfoo
, but it won't stop you writing foo.tryAs<Any, Bar>()
and it will compile whatever type foo
is.Rob Elliot
05/01/2024, 1:30 PMthis
. Is that a thing? What's the right way to express it as an idea?
(e.g. in Java particularly I would find myself subclassing builders and wishing there was a way to say that foo.withName()
returns whatever the compile time type of foo
is without having to do self referential generics)Sam
05/01/2024, 1:36 PMSam
05/01/2024, 1:39 PMRob Elliot
05/01/2024, 1:47 PMinline fun <T, reified T2 : T> T.tryAs(): T2? = this as? T2
interface Foo
interface Bar : Foo
interface Baz
val foo: Foo = TODO()
val maybeBar: Bar? = foo.tryAs<Foo, Bar>() // compiles
val baz: Baz = TODO()
val maybeBar2: Bar? = baz.tryAs<Baz, Bar>() // does not compile, Bar is not a subtype of Baz
so in principle this would behave in just the same way:
inline fun <reified T : this::class> this::class.tryAs(): T? = this as? T
val maybeBar: Bar? = foo.tryAs<Bar>() // would compile
val maybeBar2: Bar? = baz.tryAs<Bar>() // would not compile, Bar is not a subtype of Baz