I could not find an existing topic regarding this issue but maybe my keywords were simply not the right ones to search for. Regardless, here it comes.
I would love to see default types for generics in Kotlin like Rust has it:
The most prominent example that could make use of this would be Comparable<T> by changing it to Comparable<T = this> but there are more interesting use cases.
sealead class S
class A : S()
class B : S()
open class X<T : A>
class Y : X<B>()
Constructing X is not possible without specifying the concrete type right now, hence, one has to write X<A>() everywhere. This is cumbersome and repetitive and something that was clear for X from the very beginning.
open class X<T : A = A>
class Y() : A<B>()
Now it is possible to construct X without specifying A everywhere: X()
Based on this post here I guess that this is something that will be part of 1.4 or will be implemented in one of the 1.4.x releases. I haven’t seen it talked about as part of 1.4 so probably not the first release.
class MyClass<T> {
companion object {
operator fun invoke() = MyClass<Int>()
}
}
fun main() {
val foo = MyClass<String>()
val bar = MyClass() // this is a MyClass<Int>
}
In my case I have a ViewModel Class that accepts an inputDataAdapter so that a ViewModel for a specific view can provide a TypeAdapter from String to “T”. But in some or many cases a simple (String) → String inputDataAdapter could be provided as Default Lambda.
The only way that I figured out is to “dirty” cast to T. If I could provide a Default Type for “T” the dirty cast is no more needed.
class ViewModel<T: DA = String>(
val inputDataAdapter: (String) -> DA = { input -> input },
)
// instead of
class ViewModel<T>(
val inputDataAdapter: (String) -> T = { input -> input as T },
)
I need to set a default type for var value: T. I can’t use lateinit here, what should I use except of null if I want to make the variable non-nullable?
Found a nice work-around:
public suspend fun <T> Flow<T>.first(): T {
var result: Any? = NULL
collectWhile {
result = it
false
}
if (result === NULL) throw NoSuchElementException("Expected at least one element")
return result as T
}
You can use some marker object defined above the function like so:
private object EMPTY
public suspend fun <T> Flow<T>.first(): T {
var result: Any? = EMPTY
collectWhile {
result = it
false
}
if (result === EMPTY) throw NoSuchElementException("Expected at least one element")
return result as T
}
It’s the Flow original method I found to be usable. You can’t use null as it breaks the needed conditions (you need to use value!! or likewise later everywhere). NULL here is
@JvmField
@SharedImmutable
internal val NULL = Symbol("NULL")
There is nothing wrong in using !! in cases it is really needed. In this specific case you actually wouldn’t need to use it, because if (result == null) throw automatically smart-casts to not-nullable. Kotlin is that smart!
I believe the reason what they used NULL in this function is because the flow itself could pass null values, so they needed a way to distinguish situation where we collected null or not collected anything. Alternatively, they could use a separate boolean flag, but such NULL object is often easier to use and even better for performance. Drawback is that we have to use an unchecked cast, which we wouldn’t need if not using NULL.