I recently found rather strange behavior while using generics with lambdas.
Code sample:
class Generic<T>(private val optional: T? = null, private val source: (T)->Unit) {
fun doSomething(value: T) = source(value)
}
fun <T> createGeneric(optional: T? = null, source: (T)->Unit)
= Generic(optional, source)
fun test() {
val generic = createGeneric(null) { arg: Int -> println(arg) }
generic.doSomething(42)
}
Strangely, variable generic is deduced as Generic<Nothing>, so next line causes an error - even though type (Int) -> Unit is passed for parameter source.
Both true and false I guess. Yes it is inferring the type from null but that does provide information about the type. The type of null is Nothing?. Since the type of optional is T? the must be Nothing.
Normally this behavior is what we want. Nothing is more specific than Int that’s why the compiler chooses Nothing. And in most cases that’s what we want.
class Foo
class Bar : Foo()
createGeneric(Bar()) { arg: Foo -> }
we would expect the compiler to choose Bar for the generic type here, since it’s more specific.
I think the question raised on SO (in the comments of Alexey’s answer) is: should there be an exception for Nothing. Nothing is always the more specific type. But in 99.99% of all cases it does not add any information and instead just creates a problem that has to be solved by specifying the type directly.
The special case where Nothing is useful is for control flow analysis. It can also sometimes be used to declare that a parameter (with a generic type) is not used and therefor should always be null, but I have never seen that done in a real scenario.
Normally this behavior is what we want. Nothing is more specific than Int that’s why the compiler chooses Nothing . And in most cases that’s what we want.
If this would be the case, then yes it would be valid to assume. However, the problem is that neither is more specific to the other one, because any decision for T will shadow either the first or the other parameter. Nothing? is more specific than Int? , but Nothing?->Unit is less specific than Int?->Unit.
Either way, the particular shadowed parameter have to be downcasted in order to see the maximum of all available operations.
Nonetheless, Int is not a super type of Nothing?, but to Nothing. So passing arg:Int->Unit to create a Generic<Nothing?> because assigning null to arg is invalid. Even creating Generic<Nothing> is invalid because null isn’t an instance of Nothing.