Java annotation cannot be found per reflection on a Kotlin data class - java

Given this Java annotation
#Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#JacksonAnnotation
public #interface JsonProperty
and this Kotlin data class
#JsonIgnoreProperties(ignoreUnknown = true)
data class LossDocument(#JsonProperty("id") val id: String)
I would expect to find the annotation either here
LossDocument::class.java.declaredFields[0].annotations
or here
LossDocument::class.java.declaredMethods.first { it.name == "getId" }
but both have zero annotations. Is this a bug? Per 53843771, my impression is this should work. I'm using Kotlin 1.4.0.
When I declare the annotation explicitly as #field:JsonProperty("id") I can find it without problem using LossDocument::class.java.declaredFields[1].annotations.

When you're annotating a property or a primary constructor parameter, there are multiple Java elements which are generated from the corresponding Kotlin element, and therefore multiple possible locations for the annotation in the generated Java bytecode.
If you don't specify a use-site target, the target is chosen according to the #Target annotation of the annotation being used. If there are multiple applicable targets, the first applicable target from the following list is used:
param, property, field. -- Annotation Use-site Targets
In your case the annotation is placed on the constructor parameter.

Related

can I use any annotation on custom annotation

As, I know we can apply annotation like #Target, #Retention, #Documented on custom annotation.
But recently I saw #Constraint applied on custom annotation.
Can We use any annotation on custom annotation? how it works?
Each annotation can only be written in certain places. Those places are determined by the #Target meta-annotation on the annotation's definition. For example, if an annotation is declared as
#Target(ElementType.ANNOTATION_TYPE)
public #interface MyAnnotation {
...
}
then #MyAnnotation may only be written on annotation declarations. You cannot write #MyAnnotation on a field or class declaration, or on a type use. An annotation that may be written on annotation declarations is called a meta-annotation.
You can learn more about annotations from the Java annotations tutorial.

Kotlin annotation not in fieldDecleration or compiled java

For my data objects in Kotlin I have added a custom annotation for GSON to have an exclusion rule.
In the past this has worked perfectly, now it does not show up in my class reflection (this.javaClass.declaredFields[3].annotations is null) nor does it show up in the compiled java output.
I have tried different things, like upgrading my kotlin version, adding kotlin-kapt, using different #Retention types, restarting my computer (you never know) and have looked at other annotations. Those other annotations (for instance for Hibernate a #OneToOne) shows up with no issue.
Annotation definition:
#Retention(AnnotationRetention.RUNTIME)
#Repeatable
#Target(
AnnotationTarget.FIELD,
AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER,
AnnotationTarget.PROPERTY,
AnnotationTarget.VALUE_PARAMETER
)
annotation class ExcludeFromJSON
Usage in data class:
#Entity
#Table(name = "user")
class User (
var username: String = "",
var email: String = "",
#ExcludeFromJSON
private var password: String
) {}
I expect the annotation to show up in the javaClass reflection and in the compiled java code. It does neither.
Compiled password var (no annotation...):
private final var password: kotlin.String /* compiled code */`
You should qualify the annotation with the appropriate use-site target:
#field:ExcludeFromJSON
private var password: String
This will cause the annotation to be present on the Java field generated by this property.
From the Kotlin Reference regarding Annotation Use-site Targets:
When you're annotating a property or a primary constructor parameter, there are multiple Java elements which are generated from the corresponding Kotlin element, and therefore multiple possible locations for the annotation in the generated Java bytecode. [...]
[...]
The full list of supported use-site targets is:
file;
property (annotations with this target are not visible to Java);
field;
get (property getter);
set (property setter);
receiver (receiver parameter of an extension function or property);
param (constructor parameter);
setparam (property setter parameter);
delegate (the field storing the delegate instance for a delegated property).
[...]
If you don't specify a use-site target, the target is chosen according to the #Target annotation of the annotation being used. If there are multiple applicable targets, the first applicable target from the following list is used:
param;
property;
field.
The three important things to take away from that are:
Annotations in Kotlin have a certain amount of ambiguity1 regarding where they're ultimately applied. For instance, placing your annotation where you did means said annotation could be applied to one of at least five different locations: property, field, getter, setter, setter parameter.
An annotation applied to a Kotlin property is not visible on the Java side.
An annotation on a Kotlin property, without a use-site target, will only be applied to the backing field if it's not also applicable to being applied to the Kotlin property.
Your annotation has both AnnotationTarget.FIELD and AnnotationTarget.PROPERTY in its #Target annotation. Since you don't specify a use-site target, the property takes precedence—meaning the annotation is not visible to Java.
For more information regarding properties and fields, see the Properties and Fields page of the Kotlin Reference.
1. It's not technically ambiguous, as everything is well defined.

How to combine multiple annotations to single one?

I have two annotations from a framework. Often I use those two annotations both on the same field. Thus I'm trying to create a "combined" annotation that contains that both two.
But I don't know if it is possible at all:
The existing annotations (that I have no control of):
#Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
public #interface ApiParam {
String name() default "";
}
#Target({ElementType.METHOD, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
public #interface ApiModelProperty {
String name() default "";
}
My Custom annotation that I'm trying to create:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.ANNOTATION_TYPE)
#ApiParam
#ApiModelProperty
public #interface SwaggerParam {
String name() default "";
}
Result: the annotations are not applicable to annotation type.
Question: Is there any chance?
Unfortunately you can't do this since it is not possible to extend annotations.
Is there something like Annotation Inheritance in java?
When I first answered this I was initially confused by the Spring framework approach to this shortcoming whereby they use meta level annotations (such as #Component as a meta annotation for #Controller/#Configuration etc.) as a sort of workaround.
See: https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/beans.html#beans-annotation-config
Composing annotations like you did can only be done if your framework supports scanning for meta-annotations. Thus the framework not only has to scan for direct annotations but also for an annotation's meta-annotations recursively.
Multiple frameworks support this, some of which are:
junit: https://junit.org/junit5/docs/current/user-guide/#writing-tests-meta-annotations
Spring: https://www.logicbig.com/tutorials/spring-framework/spring-web-mvc/meta-annotation.html
Swagger: https://stackoverflow.com/a/53266819/1235217

default {} in #interface in Spring

Just read some source code from Spring-web-4.2.4, found that the ControllerAdvice (annotation) is pretty interesting:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Component
public #interface ControllerAdvice {
#AliasFor("basePackages")
String[] value() default {};
#AliasFor("value")
String[] basePackages() default {};
//......
}
I do not consider default {} as the new feature of Java 8?
In Java #interface is a some kind of reserved word (keyword) for defining annotations. So you can be sure that the class you listed above is an annotation definition. In this classes default keyword could be used to define default value for annotation properties. This feature was introduced at the same time as annotations were introduced in the language.
The default keyword is used in annotations to set a default value for a corresponding annotation type (it, in turn, allows you not to specify a value of this type every time you write the annotation);
{} is just an array literal which means an empty array.

How does Java #Target annotaion declare Target annotation?

I'm a newbie with Java programming. While i'm learning Java Annotation, I digged a little big deeper into the declaration of #Target in JDK and i got this
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.ANNOTATION_TYPE)
public #interface Target {
ElementType[] value();
}
What confuses me here is that #Target Annotation declares Target Annotation.
Where comes the very first #Target annotation ?
Where comes the very first #Target annotation ?
During compilation, the compiler finds (and defines/registers) the annotation type Target. It will then scan the type for annotations and find #Target which was already defined, so no issue.

Categories