Suppose we had a class with many different propert...
# language-evolution
d
Suppose we had a class with many different properties, and we want a method to be able to get all of the property values annotated with a particular annotation.
Copy code
@Target(AnnotationTarget.PROPERTY)
annotation class Special

class Example(
   @Special
   val x: String,
   @Special
   val y: String,
   @Special
   val z: String
) {

   val specials: List<String>
       get() = TODO()
}
We could write a method that uses reflection to get and invoke all properties with a particular annotation on a class:
Copy code
inline fun <reified T : Any> getSpecials(holder: T): List<String> {
   return T::class.memberProperties
        .filter { it.hasAnnotation<Special>() }
        .map { property -> property.call(holder) as String }
}

class Example(
   @Special
   val x: String,
   @Special
   val y: String,
   @Special
   val z: String
) {

   val specials: List<String>
       get() = getSpecials(this)
}
However, this requires reflection, and the bytecode and execution time will be incredibly large compared to the desired goal of:
Copy code
val specials: List<String>
   get() = listOf(x, y, z)
This could be achieved with the help of an annotation processor, assuming the values aren’t private, but there’s often a large engineering overhead when adding an annotation processor, especially if the code is edited with an IDE and the code isn’t always fully compiled. However, what if the Kotlin compiler were able to inline the entire concept of properties? The inlined code would look like this:
Copy code
val specials: List<String>
   get() = Example::class.memberProperties
       .filter { it.hasAnnotation<Special>() }
       .map { property -> property.call(this) as String }
Now, suppose that Kotlin could inline `memberProperties`:
Copy code
val specials: List<String>
   get() = inlineListOf(Example::x, Example::y, Example::z, Example::specials)
       .filter { it.hasAnnotation<Special>() }
       .map { property -> property.call(this) as String }
This would require Kotlin to have a concept of an inlined list, that automatically applies loop unrolling in the next compilation step:
Copy code
val specials: List<String>
   get() = listOfNotNull(
           if (Example::x.hasAnnotation<Special>()) Example::x.call(this) as String else null,
           if (Example::y.hasAnnotation<Special>()) Example::y.call(this) as String else null,
           if (Example::z.hasAnnotation<Special>()) Example::z.call(this) as String else null,
           if (Example::specials.hasAnnotation<Special>()) Example::specials.call(this) as String else null
       )
(Using null just for simplicity here. Some other sentinel value and boxing/unboxing would be used to preserve correct values even if the elements are nullable.) The compiler at this point knows the properties that annotations have, so it would be able to further simplify this to:
Copy code
val specials: List<String>
   get() = listOf(
           SpecialHolder::x.call(this) as String,
           SpecialHolder::y.call(this) as String,
           SpecialHolder::z.call(this) as String
       )
At this step, the compiler would identify if any properties got through the filter that weren’t actually strings, so it would become a compilation-time error rather than a runtime error. It wouldn’t catch this for open classes or interfaces, though, those would require a new compilation-time version of
as
. This finally simplifies to:
Copy code
val specials: List<String>
   get() = listOf(x, y, z)