https://kotlinlang.org logo
#getting-started
Title
# getting-started
d

David Kubecka

02/22/2023, 10:43 AM
Why the compiler is complaining here, i.e. can't auto-cast?
Copy code
enum class DateAttribute : UpcomingPaymentAttribute

data class DateKey(val startDate: LocalDate, val granularity: DateAttribute)

...
            // it is UpcomingPaymentAttribute
            when (it) {
                DateAttribute.DAY -> DateKey(LocalDate.now(), it) // here compiler complains that it is UpcomingPaymentAttribute, not DateAttribute
c

CLOVIS

02/22/2023, 10:49 AM
Is
it
a
var
? What's the compiler message?
d

David Kubecka

02/22/2023, 10:56 AM
Type mismatch: inferred type is UpcomingPaymentAttribute but DateAttribute was expected
it
is just the standard "default" variable
c

CLOVIS

02/22/2023, 10:57 AM
Could you reproduce it on https://play.kotlinlang.org/? There's not enough information to understand what the problem is
g

Goetz Markgraf

02/22/2023, 12:43 PM
Why should
it
be of type
DateAttribute
? I don’t see any cast.
it
is of Type
UpcomingPaymentAttribute
. In the
when
expression, I see that
it
is compared to a value of
DateAttribute.DAY
. But that does not include, casting
it
to
DateAttribute
. If you had a
is DateAttribute
in the
when
expression, than the compiler would cast.
d

David Kubecka

02/22/2023, 12:48 PM
I see. But if
it == DateAttribute.DAY
then surely
it is DateAttribute
🙂 Can I somehow help the compiler realize that (other than manual cast)?
c

CLOVIS

02/22/2023, 12:56 PM
No, you can override equals. If you used
===
, then sure.
g

Goetz Markgraf

02/22/2023, 1:01 PM
I was able to reproduce your situation:
Copy code
interface Parent

enum class Child : Parent {
    THREE, FOUR
}

fun doIt(param: Child) {
    println (param)
}

val a: Parent = Child.THREE

when (a) {
    Child.THREE -> doIt(a)
    Child.FOUR -> {}
}
Two solutions:
Copy code
when {
    a is Child && a == Child.THREE -> doIt(a)
or
Copy code
when (a) {
    Child.THREE -> doIt(a as Child)
I’d prefer the second variant. I agree with you that the compiler actually should be able to cast that.
c

CLOVIS

02/22/2023, 1:04 PM
I disagree with you, I much prefer the first variant. With the first variant, nothing can go wrong. With the second variant, if someone ever overrides
equals
with a dumb implementation (which would indeed be dumb, but it could happen), it'll crash with ClassCastException, which will be hell to debug.
d

David Kubecka

02/22/2023, 1:48 PM
you can override equals
That's the explanation I was looking for. Thanks! Although, the consequences are kind of unfortunate because either I will do the explicit cast/check, or go with
===
losing the exhaustiveness of
when
c

CLOVIS

02/22/2023, 1:50 PM
In this case, I would write:
Copy code
when (it) {
    is YourEnum -> when (it) {
        YourEnum.FirstElement -> …
        YourEnum.SecondElement -> …
    }
    else -> …
}
It's much easier to understand what happens, you have the smart cast and exhaustive checks, and it's probably faster (the inner
when
on the JVM can be compiled to a native
switch
)
👍 2
g

Goetz Markgraf

02/22/2023, 1:52 PM
@CLOVIS Thanx, I did not think of this. Again a good reminder, why class cast in kotlin is (almost always) a bad idea.
👍 1
c

CLOVIS

02/22/2023, 1:52 PM
Rule of thumb: if the compiler doesn't trust your
is
check, you shouldn't either
k

Klitos Kyriacou

02/22/2023, 3:31 PM
Can you give me some sample code in which a silly definition of an overridden
equals
can lead to unexpected behaviour? I couldn't get it to misbehave the way @CLOVIS suggests:
Copy code
interface Parent

object O : Parent {
    override fun equals(other: Any?): Boolean {
        return true
    }
}

enum class Child : Parent {
    ONE, TWO;
}

var o: Parent = O

fun main() {
    println(o.equals(Child.ONE)) // true
    println(o == Child.ONE)  // false!
    val n = when (o) {
        Child.ONE -> 1
        Child.TWO -> 2
        O -> 3
        else -> 4
    }
    println(n)  // 3, not 1
}
So it seems that the
when
expression will choose the enum value only when the variable is an actual enum type.
c

CLOVIS

02/22/2023, 3:37 PM
Ouch this is so much worse than I thought. On the JVM it uses the enum identity (playground), but on JS it uses the object equality (playground), so this code behaves differently depending on the platform
k

Klitos Kyriacou

02/22/2023, 5:27 PM
Upvoted. Related is the fact that on the JVM (but not on JS) the
==
operator doesn't seem to call
equals
(playground). This contradicts the language spec and the documentation.
😨 1
3 Views