I’m wondering about a couple of enum limitations I encountered and whether they can be fixed in the future
1. It is impossible to access functions/properties which are particular to an enum object:
enum class X {
A {
fun a() {}
},
B,
C;
}
// This does not compile, function is inaccessible
X.A.a()
In this case, a() is specific to A and I would only like to access it through X.A
2. It is impossible to declare a variable whose type is enum object:
// X defined as above
interface I {
val x: X
}
class Y: I {
// This does not compile
override val x: X.A = X.A
}
In this case, I want x to be specifically object A for class Y
Both limitations go away if I use interface + objects for each literal (vs. enum), but in that case I lack exhaustiveness in when, and that carries risks of its own.
I believe also that in a case of a sealed class (in other words, many objects vs a single object) no such limitations exist, so there is no “feature parity” between the two…
Well yeah, that’s the point. They are 2 different concepts with different limitations. Both of your examples can be explained with one simple fact. Each value of a enum is of the same type, therefor they all have the same methods and properties. Also you can not declare a value of type X.A as that would be the same as saying I want this argument to have the type 5. For what you are describing you want to use sealed classes as you pointed out.
You may consider a solution like this one to address the first one of your points
enum class X {
A {
override fun doSomething() {
println("Doing something cool");
}
},
B {
override fun doSomething() {
println("Doing something else");
}
},
C;
open fun doSomething() {};
}
fun main(args : Array<String>) {
X.A.doSomething();
X.B.doSomething();
X.C.doSomething();
}
Indeed I was able to solve this with sealed classes:
sealed class X {}
object A: X() {
fun a() {}
}
object B: X()
object C: X()
interface I {
val x: X
}
class Y: I {
override val x: A = A
}
This has the best of both worlds (fits my model and exhaustiveness). My faith in Kotlin is restored!
@Wasabi375 There is no such thing saying that 5 can’t be a type… e.g. in my example, A is both a value and a type. Unit is another example. This is only a limitation of the type system
I think you are confusing 3 different concepts: Type, Class and Object. In your enum A is an object and if I understand the way the jvm handles it also a class but A does not generate it’s own type. The type of A when you use it is still X. That’s why you can’t access members specific to A only outside of A and also why you can’t have a field of type X.A.
I sadly don’t have any links to the way enums work internally. So that’s just my own understanding after years using Java and now Kotlin.
@Wasabi375 Check my solution with sealed class. A is an object, but it is used as a type too (in class Y). What you’re talking about are internals of the JVM which, while imposing a limitation on Kotlin, are not a definite authority over what can be and can’t be a type. 5 can be used as a type in the languages with very flexible type systems e.g. TypeScript
Got it, thanks! It was confusing cause it is possible to add a function, and it is public, but it’s still inaccessible from the outside… without knowing the internals it is a reasonable expectation to me for the function to be callable
I guess it is. I never thought about it as I don’t usually use enums often with different functions. I like to use sealed classes once they get more complicated than storing a few variables.