Does anyone know why the following does not compil...
# getting-started
t
Does anyone know why the following does not compile, and whether or not any workarounds for it exist? Replacing the generic type T with Any does seem to work, but I can't really explain why this doesn't.
Copy code
fun <T> test(bar: T) {
    var foo = bar
    if (foo is Boolean) {
        foo = !foo
    }
    println(foo)
}
The error here is the following on the
foo = !foo
line:
Copy code
Type mismatch.
Required: T
Found: Boolean
e
foo
has the same type of
bar
, so it's a
T
the compiler doesn't do the smart cast
j
The inferred type of the variable
foo
is
T
. I guess what happens is that the smart cast only applies to the scope of the
if
, but doesn't apply to the declaration that's outside this scope.
e
it seems related to the fact that
foo
is being reassigned inside the if block, if we define a new var, it compiles
Copy code
fun <T> test(bar: T) {
    val foo = bar
    if (foo is Boolean) {
        val foo2 = !foo
    }
    println(foo)
}
j
Yes, the value
foo
is smart cast. The problem is the assignment to the variable from the outer scope. I fail to see a case where the assignment wouldn't be valid, but I guess that's just a limitation of the type checker (it doesn't deduce from the condition that the
foo
variable's type can be assigned boolean values).
t
I see, that's a shame. For whatever it's worth we can still perform a manual cast to T (
foo = !foo as T
)
j
Yes. Note that, even within the
if
, the type of the
foo
variable is still
T
because you should still be able to assign other `T`s to it, regardless of the current value's type. The information that the type checker is missing is that
T
is assignable from
Boolean
.
t
That's a good point, hadn't thought of that
h
The information that the type checker is missing is that
T
is assignable from
Boolean
.
... which may not be trivial to deduce. I think it only works because
Boolean
cannot be inherited from. Example where the assignment would (and should) fail:
Copy code
interface A
interface B {
    operator fun not() = object : B {}
}
class AB: A, B

fun <T: A> test(bar: T) {
    val foo = bar
    if (foo is B) {
        val foo2 = !foo
        println(foo2 is A) // false
        foo = foo2 // fails
    }
}
j
Oh right! Thanks, I was looking exactly for this kind of counter-example (but I stayed too focus on
Boolean
to find it 😆)
t
Perfect, that completely clears it up, thanks everyone!