getAnnotations on Scala class fields

2019-07-15 10:29发布

问题:

I'm trying to use a Java library (JOhm) with Scala and noticed it fails when the lib tries to read the annotations on the fields of my Scala classes with something like field.isAnnotationPresent(Id.class).

The annotations are defined in Java with:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Id {

}

So they should be available on runtime. However, if I try:

scala> import redis.clients.johm._
import redis.clients.johm._

scala> class myClass(@Id var id: java.lang.Long)
defined class myClass

scala> new myClass(id = null)
res0: myClass = myClass@5157cfbc

scala> res0.getClass.getDeclaredFields
res1: Array[java.lang.reflect.Field] = Array(private java.lang.Long myClass.id)

scala> res0.getClass.getDeclaredFields.map(_.getAnnotations)
res2: Array[Array[java.lang.annotation.Annotation]] = Array(Array())

Then I manage to get the fields (thanks to StackOverflow users…) but I still cannot access the annotations. What did I do wrong?

回答1:

Thanks to 'soc' for reference provided, there is a definitive answer: "By default, annotations on (val-, var- or plain) constructor parameters end up on the parameter...".

The annotation in question is defined for FIELD elements with run-time retention, while it's being applied to a scala constructor argument (PARAMETER).

I had the same situation recently trying to use a JAXB FIELD level @XmlElement annotation on a scala constructor "var" parameter - didn't work for me either.

I solved my JAXB issue by defining a distinct field in the class, and initializing it from the constructor's parameter list (eliminating the 'val/var' keyword). In your case that might look like:

class myClass(_id: java.lang.Long) {
    @Id var id = _id
}

The reference suggests two other alternatives:

  1. Changing(adding) element type of the annotation to (include) PARAMETER; this is only viable if the source is available.

  2. Use the target package in 2.9.2, which has been renamed to meta package for 2.10, to re-classify (re-target) the annotation to an underlying field (or other option).