is it possible to "cast" an upper bound on a gener...
# announcements
b
is it possible to "cast" an upper bound on a generic type? i have a method:
Copy code
override fun <T : Any> getterFor(valueType: KClass<T>): (String) -> T {
    if (valueType.isSubclassOf(Enum::class)) {
        // Need to call a method which expected KClass<T> where T : Enum<T>
        enumHelper(valueType)
    }
    ...
}
i can detect it's an enum, but can't figure out how to cast 'valueType' correctly such that it can be passed to a function which expects a
KClass<T>
where
T : Enum<T>
s
might be a silly question, but why not just bound
T : Enum<T>
in `getterFor`’s signature instead of
Any
?
b
i've got to handle a bunch of other types, so can't bound it there 😕 (left those out to try and simplify the example, i guess i should've noted that)
s
Ah, I see
runtime type checking is a truly cursed endeavor, and I don’t think smart casting can help in this case 😞 you’ll likely have to do some unsafe casting by hand if you really need to be able to take
Any
I figure you’ve probably tried the alternatives but I’d be remiss if I didn’t at least mention considering overloaded or type-named methods
b
yeah...i'm actually ok with 'unsafe' casting (i already have to in that method--i'm not too worried since i'm checking things manually). but do you know how i'd do even the unsafe cast?
i can't use type-named here, but if an overload would work that'd be perfect, actually...i'll check that
it does look like an overload would work...i have to put it in my interface, in this example, but maybe that's the only way
(i also had to put a dummy argument since otherwise the overloads aren't distinguished)
s
due to type erasure? you might be able to get away with using
@JvmName
to distinguish the methods
b
damn, actually, i don't think it'll resolve the right one.
if i define:
Copy code
class Blah {
    fun <T : Any> getterFor(valueType: KClass<T>) {
        println("non-enum")
    }

    fun <T : Enum<T>> getterFor(valueType: KClass<T>, dummy: String = "") {
        println("enum")
    }
}
and call:
Copy code
val x = Blah()

    x.getterFor(Int::class)
    x.getterFor(Foo::class)
that works
but if i've got a generic method in between (which is my use case) like:
Copy code
inline fun <reified T : Any> blah() {
    val x = Blah()

    x.getterFor(T::class)
}
and call it via:
Copy code
blah<Int>()
    blah<Foo>()
then it calls the non-enum both times
(Foo is an enum class)
and i didn't want to have the methods named different because i didn't want the call site to have to care
but maybe can't get around that
s
hmm
I think the type system can’t really account for this specific case -
T : Any
will match any non-null type, which Enum falls under
b
yeah
hm, and i can't put the test for enum (to call one function or the other) in the inlined helper, even, as then i run into the same problem: changing the upper bound on
T
it's just surprising to me that: given a
KClass<T>
that i've determined is an enum via
isSubclassOf(Enum::class
, there's no way to cast it to a
KClass<X : Enum<X>>
s
Enums being recursively generic makes this really difficult unfortunately
b
yeah that is a bit weird
s
would it possible to to have your
enumHelper
function take
KClass<Enum<*>>
? that’s a type you can legally (unchecked) cast to
b
the method i'm trying to call is in a library, and the signature is
public <T extends Enum<T>> T getEnum(Class<T> enumClass, String path);
s
yikes
b
it eventually calls Enum.valueOf, which is defined similarly
c
Another suggestion might be to consider Algebraic Data Types, Kotlin supports them through Sealed Classes, which are somewhat like supercharged
enum
s. Any time you know that the possible genera of types is limited, constrain them in an outer type.
b
yeah, the use case here is i've got a layer on top of a configuration library. the underlying config library supports parsing a config value from a file as a defined enum. i ran into this issue when trying to add support for that in my layer.
if i can't get it to work, then yeah i'll probably have to end up saying: don't use enums 🙂