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

ursus

03/26/2024, 7:54 PM
when I print
MyEnum.entries
in the debugger, I see a different instance everytime Wasn't the point of
entries
to get the same instance always?
r

Ronny Bräunlich

03/27/2024, 6:43 AM
The point of
entries
is to have an immutable representation of the enum values. In contrast, the array you receive when calling
values
is not immutable. Looking at the docs of
EnumEntries
you can see that it's a special list, so I suppose you get a new list every time (
enumEntries()
returns definitely a new one)
g

gildor

03/27/2024, 7:52 AM
It still was discussed during KEEP as one of advantages
Arrays are mutable by default, meaning that each call to
values()
always has to allocate a new instance of the array. The API shape does not indicate that fact, often leading to hidden performance issues in both Kotlin and Java. It is hard for library authors to determine whether an arbitrary call to
values()
may lead to a performance bottleneck as the profile depends on the "hotness" of the method, not on the enum's characteristics, effectively forcing authors to apply a workaround or leave it as a potential performance issue.
Now I re-reading it, and it's indeed not explicitly mentions that new solution will not create new instance every time, but I would imply it from motivation, because it's indeed may cause bottleneck
ah, actually it's not the same, EnumEntriesList is just wrapper around entries, so it's still way faster, doesn't require array copy
and making new instance every time allows to make it thread safe (so every thread has own EnumEntriesList
So I now see why it was made this way
s

Szymon Jeziorski

03/27/2024, 9:37 AM
How could
EnumEntriesList
not be thread safe anyways if it's immutable from the outside of internal implementation? From what I tested I could see that for Kotlin enums one instance of
EnumEntriesList
is created and being reused even when accessed from different threads - https://pl.kotl.in/W6_KU3anc It makes sense as design note mentions entries as static field with value instantiated on class initialization. For Java enums from what I tested instances of
EnumEntriesList
are shared but there is no single-instance guarantee, as actual Java classes cannot be directly altered by compiler. I found this class with caching mechanism: https://github.com/JetBrains/kotlin/blob/master/compiler/ir/backend.jvm/codegen/sr[…]in/backend/jvm/codegen/EnumEntriesIntrinsicMappingsCacheImpl.kt Correct me on this one, but I would guess that you seeing different instance each time in debugger would have to do more with how debugger loads classes than with the actual implementation. Also, I didn't see any difference in behavior of
Enum.entries
and
enumEntries<T>
, @Ronny Bräunlich where did you found information about
enumEntries()
returning new instance each time?
👍 1
g

gildor

03/27/2024, 9:40 AM
Yes, you right, agree on this, EnumEntreies must be thread safe already anyway, there is no mutability, and iterators already safe
I believe about enumEntries is just from source code:
Copy code
@PublishedApi
@SinceKotlin("1.8")
internal fun <E : Enum<E>> enumEntries(entries: Array<E>): EnumEntries<E> = EnumEntriesList(entries)
Buty it's interanl version
not exposed by public API
so in real code it should call intrinsic one
r

Ronny Bräunlich

03/27/2024, 9:55 AM
Check the sources of
kotlin.enums.enumEntries()
s

Szymon Jeziorski

03/27/2024, 10:05 AM
enumEntries()
calls
enumEntriesIntrinsic()
which has intrinsic implementation with note about it in source sets:
Copy code
internal actual inline fun <reified T : Enum<T>> enumEntriesIntrinsic(): EnumEntries<T> {
    /*
     * Implementation note: this body will be replaced with `throw NotImplementedException()` the moment
     * all backends starts intrinsifying this call.
     */
    return enumEntries(enumValues<T>())
}
Therefore
enumEntries(enumValues<T>())
is not actually called each time you invoke
enumEntries()
, compiler has its own internal implementation for it
k

Klitos Kyriacou

03/27/2024, 10:58 AM
Indeed, this prints `true`:
Copy code
enum class E { A, B }

fun main() {
    val a = E.entries
    val b = E.entries
    println(a === b) // true
}
But if you set a breakpoint anywhere in the code, and do "Evaluate expression" and type "E.entries" in the text box, then you can see that every time you press Evaluate you get a different instance. This seems to only happen in the debugger.
u

ursus

03/27/2024, 12:01 PM
@Klitos Kyriacou exactly, super odd
3 Views