ursus
03/26/2024, 7:54 PMMyEnum.entries
in the debugger, I see a different instance everytime
Wasn't the point of entries
to get the same instance always?Ronny Bräunlich
03/27/2024, 6:43 AMentries
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)gildor
03/27/2024, 7:52 AMgildor
03/27/2024, 7:54 AMArrays are mutable by default, meaning that each call toalways 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 tovalues()
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.values()
gildor
03/27/2024, 7:55 AMgildor
03/27/2024, 7:56 AMgildor
03/27/2024, 7:57 AMgildor
03/27/2024, 7:57 AMSzymon Jeziorski
03/27/2024, 9:37 AMEnumEntriesList
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?gildor
03/27/2024, 9:40 AMgildor
03/27/2024, 9:41 AM@PublishedApi
@SinceKotlin("1.8")
internal fun <E : Enum<E>> enumEntries(entries: Array<E>): EnumEntries<E> = EnumEntriesList(entries)
gildor
03/27/2024, 9:42 AMgildor
03/27/2024, 9:42 AMgildor
03/27/2024, 9:42 AMRonny Bräunlich
03/27/2024, 9:55 AMkotlin.enums.enumEntries()
Szymon Jeziorski
03/27/2024, 10:05 AMenumEntries()
calls enumEntriesIntrinsic()
which has intrinsic implementation with note about it in source sets:
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 itKlitos Kyriacou
03/27/2024, 10:58 AMenum 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.ursus
03/27/2024, 12:01 PM