arekolek
08/02/2019, 11:41 AMsealed class Item {
data class Foo(val id: Int) : Item()
object Bar : Item()
}
fun List<Item>.trim() = dropLastWhile { it is Item.Bar || it.id == IGNORED_ID }
It works if I do it !is Item.Foo || it.id == IGNORED_ID
though, which is understandablediesieben07
08/02/2019, 11:43 AMarekolek
08/02/2019, 11:43 AMdiesieben07
08/02/2019, 11:44 AMmarstran
08/02/2019, 11:53 AMit !is Item.Foo
is false, then it
must be Item.Foo
. Even if you would later add another subclass to Item
.
If it is Item.Bar
is false however, then it
can be any other subclass of Item
. The smart cast would break if you added a new subclass to Item
.diesieben07
08/02/2019, 11:55 AMItem.Foo
is the only type mentioned in your type check, so it's the only type that can be smart-casted towbertan
08/02/2019, 12:34 PMit !is Item.Foo || it.id == 999
if (it instanceof PathTest.Item.Foo && ((PathTest.Item.Foo)it).getId() != 999) { ... }
In my guess, the way it "optimizes" it, allows the smart cast to be in place and do the second check.
While the it is Item.Bar || it.id == IGNORED_ID
will probably "optimize" in a way it cannot "smart" cast to the second expression as it probably didn't rule out the type.
Tweaking a little your example to:
sealed class Item(val name: String) {
data class Foo(val id: Int) : Item("foo")
object Bar : Item("bar")
}
and making it is Item.Bar || it.name == "other"
it will compile and generate:
if (!(it instanceof PathTest.Item.Bar) && !Intrinsics.areEqual(it.getName(), "other")) {...}
So we can see how it "optimizes" the first expression, while the one where it "works" it checks if it is an instaceof Item.Foo
, this being true will check the second expression knowing the instance is a Foo
. In this second example it simply check if isn't Bar
, and when reaching the second expression what it knows is the class isn't a Bar
but could be anything else (not exactly a Foo
).