El objetivo principal de esta pregunta es comprender la implementación y por qué es así. Por supuesto, también sería muy apreciada una solución o una solución alternativa...
Dado este ejemplo:
enum class SomeEnum(val customProp: String) { FOO("fooProp"), BAR("barProp"); } @Target(AnnotationTarget.FUNCTION) @Retention(AnnotationRetention.SOURCE) annotation class TheAnnotation( val targetValue: String ) @TheAnnotation(targetValue = SomeEnum.FOO.customProp) fun testFun() { }La compilación da como resultado el siguiente error:
SomeEnum.kt: (14, 30): un argumento de anotación debe ser una constante de tiempo de compilación
Por razones obvias, los valores de anotación (junto con otros) deben ser constantes de tiempo de compilación, lo que tiene sentido de muchas maneras diferentes. Lo que no me queda claro es por qué el compilador no trata customProp como una constante.
Si las enumeraciones se definen como conjuntos de información finitos y cerrados, en mi opinión, solo deberían ser mutables en tiempo de compilación, también conocido como "constante de tiempo de compilación". Para el caso improbable de que las enumeraciones de alguna manera sean modificables en tiempo de ejecución en Kotlin, eso también respondería la pregunta.
Apéndice:
El valor de enumeración (por ejemplo SomeEnum.FOO ) en realidad se trata como una constante de tiempo de compilación. La prueba es que se compila el siguiente fragmento ligeramente modificado:
enum class SomeEnum(val customProp: String) { FOO("fooProp"), BAR("barProp"); } @Target(AnnotationTarget.FUNCTION) @Retention(AnnotationRetention.SOURCE) @MustBeDocumented annotation class TheAnnotation( val targetValue: SomeEnum ) @TheAnnotation(targetValue = SomeEnum.FOO) fun testFun() { }las enumeraciones se definen como conjuntos finitos y cerrados de información, en mi opinión, solo deberían ser mutables en tiempo de compilación
En realidad no. Una clase de enumeración es solo un tipo especial de clase, que no le permite crear nuevas instancias que no sean las que nombra en la declaración, además de un montón de azúcares sintácticos más. Por lo tanto, como una clase normal, puede tener propiedades cuyos valores solo se conocen en tiempo de ejecución y propiedades que son mutables (aunque esto suele ser una muy mala idea).
Por ejemplo:
enum class Foo { A, B; val foo = System.currentTimeMillis() // you can't know this at compile time! }Esto básicamente se desazuca en:
class Foo private constructor(){ val foo = System.currentTimeMillis() companion object { val A = Foo() val B = Foo() } }(El código generado real tiene un poco más de cosas que esto, pero esto es suficiente para ilustrar mi punto)
A y B son solo dos (y las únicas dos) instancias de Foo . Debería ser obvio que Foo.A no es una constante de tiempo de compilación * , y mucho menos Foo.A.foo . Puede agregar un bloque de init en Foo para ejecutar código arbitrario. Incluso podrías hacer foo a var , permitiéndote hacer cosas horribles como:
Foo.A.foo = 1 // now every other code that uses Foo.A.foo will see "1" as its valueTambién puede preguntarse por qué no implementaron una enumeración más restringida que no le permite hacer estas cosas y es una constante de tiempo de compilación, pero esa es una pregunta diferente .
Ver también: La especificación de idioma
* Aunque todavía puedes pasar Foo.A a una anotación. Para una anotación, Foo.A es una constante de tiempo de compilación, porque todo lo que tiene que hacer la anotación es almacenar el nombre "Foo.A", no el objeto al que se refiere, que debe calcularse en tiempo de ejecución.