Hi guys, I don't quite understand some use cases o...
# getting-started
s
Hi guys, I don't quite understand some use cases of generics within stdlib and how they work. Example:
Copy code
public inline fun <R, T : R> Result<T>.getOrElse(onFailure: (exception: Throwable) -> R): R
From the signature I would think that since
T
is of type
R
or its subtype, on
Result<SubClass>
instance I could invoke
getOrElse
only by passing lambda returning value of type which is super type to
SubClass
. Below code compiles perfectly fine, and I'm not sure why:
Copy code
val x: Result<CharSequence> = runCatching { "string value" }

val z = x.getOrElse { 123 }
it would be logical for me that only lambdas returning super type of
CharSequence
should be accepted, but I can pass whatever return type I want and this still works fine, it just makes inferred type of
z
Result<Any>
. Am I missing something or should this not be expected behavior? Also, I cannot understand reason behind
T : R
within function's declaration, if anything
R : T
would be more logical to my understanding.
e
because the type is declared
class Result<out T>
, a
Result<CharSequence>
is a
Result<Any>
s
Okay but this still doesn't clarify to me why is such call possible from the compiler's point. Maybe let's take another example without classes having declaration site variance:
Copy code
fun <R, T : R> genericFunction(first: T, second: R): R = second

open class BaseClass(val name: String)

class SubClass(val secondName: String, name: String) : BaseClass(name)

val base = BaseClass("name")
val derived = SubClass("secondName", "name")

val firstCall = genericFunction(base, derived) // inferred type is BaseClass 
val secondCall = genericFunction(derived, base) // inferred type is BaseClass
val thirdCall = genericFunction(base, "string") // inferred type is Any
it would be logical for me that third call shouldn't be possible as
BaseClass
is not a subtype of
String
Could you advice further?
e
the third call is
genericFunction<Any, BaseClass>()
. there's no reason the compiler needs to choose the narrowest possible type for each generic parameter in isolation - in fact, in many cases it must not. instead, it tries to find the narrowest type that satisfies the expression.
s
Ooh, that makes perfect sense now, somehow didn't think about it! Thanks so much!