If I have a generic function, is there a better wa...
# codingconventions
g
If I have a generic function, is there a better way to type-bound it with unrelated types other than checking the instance of T to each possible bound? This is unrelated types like "could be an int, string, boolean, long, etc"
j
Not sure what you mean, could you give an example?
g
Copy code
inline fun <reified T> doSomething(arg1: T) {
    when (T::class) {
        Boolean::class -> {
            
        }
        Int::class -> {
            
        }
    }
}
I was wondering if there was a better language convention to do something like above where the compiler could smart-cast arg1 from T into each type since you just checked it, or if there was a way to arbitrarily type-bound the generic.
s
you want union types, which Kotlin does not have
the closest you can get would be to used a sealed class and to enumerate the expected types within its hierarchy
Copy code
sealed class ValidTypes {
  class VtBool(val value: Boolean) : ValidTypes()
  class VtInt(val value: Int) : ValidTypes()
}

fun doSomething(arg1: ValidTypes) {
  when (arg1) {
    is ValidTypes.VtBool -> {
      
    }
    is ValidTypes.VtInt -> {
      
    }
  }
}
(and unfortunately this can't be combined with value classes, as they cannot inherit from any class)
sum types in general are not something the Kotlin language committee has historically sought to prioritize, despite the language having implicitly supported intersection types for some time now. Only recently has there been any official support for denoting a type intersection, but it is limited to intersections with Any (i.e.
T & Any
), in order to support denoting a type as "definitely not nullable"
j
I mean, why not just use as many overloads as there are branches in the when? What's the benefit of using a single function? Since your
when
branches are known statically anyway, it looks like you just need:
Copy code
fun doSomething(arg1: Boolean) {
    // whatever was in the Boolean branch of the when
}
fun doSomething(arg1: Int) {
    // whatever was in the Int branch of the when
}
// other overloads for other branches
g
Sure, but this was part of a much bigger UI function so I was trying to keep a single widget contained to a single function for clarity, but understood as it seems like the branches are the best way
j
The thing with the single function is that it still contains disjoint blocks in the
when
, so it's effectively multiple functions next to each other, just wrapped in a bit of boiler plate, which I personally think makes it harder to read, not easier
g
That's fair, I'll split it out and see what it looks like. I still think it'd be a nice feature to have some day after more pressing things are implemented where the compiler could smartcast when you type-check a generic
s
I mean, it already can? you have to switch on the thing you want to smart-cast, though
Copy code
fun <T> doSomething(arg1: T) {
  when (arg1) {
    is Boolean -> arg1.not()
    is Int -> arg1.inc()
  }
}
overloads still make the most sense here imo