Inherited annotations and other reflections enchancements


#1

I am rewriting some of my java code int Kotlin and found a few deficiencies in annotation processing via kotlin.reflect.

  1. Repeated annotations are not supported. The discussion on tracker points to the fact that repeated annotations are used rarely. But still they are used. Repeated annotations could be declared via array of annotations as I’ve pointed here. Still, reflections lack tools to work with repeated annotations. One could easily add those tools himself, but probably those tools should be in the library.
  2. Kotlin does not support inherited annotations. I understand that usage of annotations from superclass is quite rare, but it can happen (I myself use it a lot). I’ve opened a ticket in tracker for that: https://youtrack.jetbrains.com/issue/KT-22265.

And now something beyond java.

  1. In Java one can’t find if method overrides another method. So one can’t inherit annotations for methods. Kotlin currently also does not support it, but it could. It would be easy to do if reflections would provide override flag. The same goes for properties.
  2. It seems that kotlin currently does not support providing class by String name, everyone uses Java calls for that. I think it should. It also could provide ways to find class members like properties and functions by name like KProperty.forName('my.package.MyClass.myProperty').

#2

Hi!

Repeated annotations are not supported.

Repeated annotations should be supported in Kotlin reflection, i.e. KAnnotatedElement.annotations should return all annotations, including repeatable (wrapped in the corresponding container) for Java elements. What’s missing is the support in the language and here’s the issue for that: https://youtrack.jetbrains.com/issue/KT-12794

Kotlin does not support inherited annotations.

Thanks for the issue!

In Java one can’t find if method overrides another method. So one can’t inherit annotations for methods. Kotlin currently also does not support it, but it could. It would be easy to do if reflections would provide override flag. The same goes for properties.

There are plans to expose the whole override tree in the Kotlin reflection API, I’ve reported an issue: https://youtrack.jetbrains.com/issue/KT-22279

It seems that kotlin currently does not support providing class by String name, everyone uses Java calls for that. I think it should. It also could provide ways to find class members like properties and functions by name like KProperty.forName('my.package.MyClass.myProperty').

Currently Class.forName("...").kotlin for looking up classes is an OK workaround, but here’s an issue: https://youtrack.jetbrains.com/issue/KT-10440. Locating other declarations by name is not enough, you may need to also specify types of parameters to disambiguate overloads, and it’s not quite clear at this point how to do that conveniently.


#3

Kotlin @Repeatable annotation does not support providing name for container class like Java one does, so it is not clear how one can use it at this stage.

For properties it should be quite straight forward since class is fully determined by its name and it can have only one property with given name. Companion objects complicate things a bit, but it could be avoided (say ).

As for function definition, the way Java does that (using vararg with types) seems to be reasonable.


#4

Yes, @kotlin.annotation.Repeatable currently only works with source-retained annotations. Support of binary/runtime-retained annotations is in the scope of KT-12794, as I mentioned.

What I meant was to make it clear that this is not strictly a problem of Kotlin reflection (for it works correctly with repeatable annotations on Java elements), but a bigger problem, that the Kotlin language does not support repeatables as well as Java does.

There are member extension properties, which can have the same name but different types of extension receiver.

The problem is that the only sane way to specify function signatures is through erasure of its parameter types, as Java does. But Kotlin can have functions with the same name and erasure in the same class/package, for example (from the standard library):

@JvmName("sumOfInt")
fun Iterable<Int>.sum(): Int

@JvmName("sumOfLong")
fun Iterable<Long>.sum(): Long

Here, both functions are named sum and both have one parameter that is erased to Iterable. So, erasure is not enough.

Taking the vararg of KType in this hypothethical API doesn’t look feasible either, because

  1. there’s no language support for creating KType instances yet, so you’ll have to use something like ::class.createType(...) for every type, which is inconvenient
  2. it’s unclear to construct the KType instance for a parameter whose type mentions a type variable, e.g.
    fun <T> foo(t: T): T
    
    Here, the type of foo's parameter is T, which is the type variable of foo. And it’s impossible to create the KType instance without having the KTypeParameter instance for T, which can only be obtained from the KFunction for foo to begin with. So, to locate a function you need to pass parameter types, and parameter types depend on the function’s type variables, and to get function’s type variables you need to already have that function, which of course makes no sense at all.