`inline fun <reified T> Any.tryAs(): T? = this as?...
# getting-started
y
inline 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
?
e
there isn't, the built-in
filterIsInstance
extension has the same problem of having no warning
y
oh right - I noticed this at some point. oh well.
y
thank you
r
Does
inline fun <T, reified T2 : T> T.tryAs(): T2? = this as? T2
meet the safety needs? Unfortunately it's ugly:
Copy code
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>
.
s
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>
.
This 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 be
Any
. To match the behaviour of the compiler warning I think you'd need something like the secret
@Exact
annotation.
r
Oh I see... of course it does work if you specify both and the first type is the compile time type of
foo
, but it won't stop you writing
foo.tryAs<Any, Bar>()
and it will compile whatever type
foo
is.
In Kotlin & Java I've often wished for a magic "self" type which represents the compile time type of
this
. 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)
s
A self type is exactly the right way to describe it, yeah. Super useful for fluent interfaces and builders. Looks like it's been in YouTrack for a while: https://youtrack.jetbrains.com/issue/KT-6494
Does it help here, though?
r
I think so:
Copy code
inline 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:
Copy code
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