I was expecting to get an unchecked cast warning h...
# compiler
e
I was expecting to get an unchecked cast warning here where I do
k as C
, especially since I got an error when I tried
lookup[C::class] = factory
despite
C
being guaranteed to be a
K
. Anyone know why I didn't get a warning?
Copy code
public class FooFactory<K : FooKey> {
  @PublishedApi internal val lookup: MutableMap<KClass<out K>, (K) -> Foo<out K>> = mutableMapOf()

  public inline fun <reified C : K> addFactory(
    noinline factory: (C) -> Foo<out C>
  ) {
    lookup[C::class] = { k ->
      factory(k as C)
    }
  }
}
d
C
is
reified
parameter, so it will be inlined to specific type argument on each call site, which makes this cast not unchecked
e
Even though the
k
parameter to the lambda is typed as
K
? So if
C
was
FooKey.Bar
wouldn't the cast be
k: K as FooKey.Bar
?
d
Yes, that's right And because of type erasure, at runtime it will be
k: FooKey as FooKey.Bar
e
But then I could do
lookup[FooKey.Bar::class]?.invoke(FooKey.Baz)
which I would think would result in a ClassCastException
d
Not sure I understand what you mean Could you provide the full snippet?
e
That's all the code I currently have, but theoretically I could have
Copy code
sealed interface FooKey {
  data object Bar : FooKey
  data object Baz : FooKey
}
and because I wrap the factory with a cast that assumes
K
is
C
, I could later retrieve a factory for
C
, but pass it something that isn't
C
as the
K
parameter which would cause a ClassCastException
d
Well, actually any
as
cast may cause
ClassCastException
And in your original code there are type constraints, which are checked at compile time
Copy code
C <: K <: FooKey
So you can not pass such
C
which won't be subtype of
K
e
So then why is it an error to say
lookup[C::class] = factory
?
d
Copy code
lookup (value): (K) -> Foo<out K> = Function1<K, Foo<out K>>
       factory: (C) -> Foo<out K> = Function1<C, Foo<out K>>
Function1
declared as
Function1<in T, out R>
factory
may be subtype of
lookup
if
in
argument of
factory
is supertype of the corresponding argument of
lookup
, which is not correct (
C <: K
, not vice versa)
e
Ah didn't realize that the parameter is
in
but I guess that makes sense 😅
d
Actually, Kotlin type system just can't express the relation between key and value, which you put in the
lookup
map You has a relation that type of value depend on the type of specific key, which is possible only in system with dependent types (like in Agda or Arend)
Copy code
lookup: MutableMap<K' : K, KClass<out K'>, (K' -> Foo<out K')>
Sorry, don't remember exact notation which makes such definitions clear
e
Thanks, it's pretty clear now. I was hoping that using the class type parameter would help with this, but I guess it just doesn't work like that
👍 1