The target of Java annotations used in Scala code has changed in some scenarios. It looks like the behaviour in Scala 2.11 was slightly incorrect if the containing case class was defined within a trait. From 2.12 onwards the behaviour has changed significantly.
Taken from the scala.annotation.meta
API
reference:
When defining a field, the Scala compiler creates up to four accessors for it: a getter, a setter, and if the field is annotated with
@BeanProperty
, a bean getter and a bean setter....
By default, annotations on (
val
-,var
- or plain) constructor parameters end up on the parameter, not on any other entity. Annotations on fields by default only end up on the field....
The target meta-annotations can be put on the annotation type when instantiating the annotation. In the following example, the annotation @Id will be added only to the bean getter getX.
import javax.persistence.Id class A { @(Id @beanGetter) @BeanProperty val x = 0 }
The test harness applies the Java annotation @Info
to the fields of two case classes - one defined in a separate
compilation unit (TestBean
) and one within a trait (WrappedBean
). It then uses the Java reflection API to test
where the annotation has been applied.
The test harness can be run from sbt using:
sbt +run
Test Case | Expected | 2.11 | 2.12 | 2.13 |
---|---|---|---|---|
TestBean.defaultTarget |
C | C | C | C |
TestBean.fieldTarget |
F | F | F | F |
TestBean.getterTarget |
M | M | M | M |
TestBean.beanPropertyDefault |
C | C | C | C |
TestBean.beanPropertyField |
F | F | F | F |
TestBean.beanPropertyGetter |
M | M | M | M |
TestBean.beanPropertyBeanGetter |
B | B | B | B |
WrappedBean.defaultTarget |
C | - | M | M |
WrappedBean.fieldTarget |
F | F | FMC | FMC |
WrappedBean.getterTarget |
M | M | M | M |
WrappedBean.beanPropertyDefault |
C | - | MB | MB |
WrappedBean.beanPropertyField |
F | FC | FMCB | FMCB |
WrappedBean.beanPropertyGetter |
M | MC | MC | MC |
WrappedBean.beanPropertyBeanGetter |
B | B | B | B |
Key:
- C: constructor
- F: field
- M: getter method
- B: bean getter method
=== TestBean ===
defaultTarget:
field: false
method: false
constructor: true
fieldTarget:
field: true
method: false
constructor: false
getterTarget:
field: false
method: true
constructor: false
beanPropertyDefault:
field: false
method: false
getter: false
constructor: true
beanPropertyField:
field: true
method: false
getter: false
constructor: false
beanPropertyGetter:
field: false
method: true
getter: false
constructor: false
beanPropertyBeanGetter:
field: false
method: false
getter: true
constructor: false
=== WrappedBean ===
defaultTarget:
field: false
method: false
constructor: false
fieldTarget:
field: true
method: false
constructor: true
getterTarget:
field: false
method: true
constructor: false
beanPropertyDefault:
field: false
method: false
getter: false
constructor: false
beanPropertyField:
field: true
method: false
getter: false
constructor: true
beanPropertyGetter:
field: false
method: true
getter: false
constructor: true
beanPropertyBeanGetter:
field: false
method: false
getter: true
constructor: false
=== TestBean ===
defaultTarget:
field: false
method: false
constructor: true
fieldTarget:
field: true
method: false
constructor: false
getterTarget:
field: false
method: true
constructor: false
beanPropertyDefault:
field: false
method: false
getter: false
constructor: true
beanPropertyField:
field: true
method: false
getter: false
constructor: false
beanPropertyGetter:
field: false
method: true
getter: false
constructor: false
beanPropertyBeanGetter:
field: false
method: false
getter: true
constructor: false
=== WrappedBean ===
defaultTarget:
field: false
method: true
constructor: false
fieldTarget:
field: true
method: true
constructor: true
getterTarget:
field: false
method: true
constructor: false
beanPropertyDefault:
field: false
method: true
getter: true
constructor: false
beanPropertyField:
field: true
method: true
getter: true
constructor: true
beanPropertyGetter:
field: false
method: true
getter: false
constructor: true
beanPropertyBeanGetter:
field: false
method: false
getter: true
constructor: false
=== TestBean ===
defaultTarget:
field: false
method: false
constructor: true
fieldTarget:
field: true
method: false
constructor: false
getterTarget:
field: false
method: true
constructor: false
beanPropertyDefault:
field: false
method: false
getter: false
constructor: true
beanPropertyField:
field: true
method: false
getter: false
constructor: false
beanPropertyGetter:
field: false
method: true
getter: false
constructor: false
beanPropertyBeanGetter:
field: false
method: false
getter: true
constructor: false
=== WrappedBean ===
defaultTarget:
field: false
method: true
constructor: false
fieldTarget:
field: true
method: true
constructor: true
getterTarget:
field: false
method: true
constructor: false
beanPropertyDefault:
field: false
method: true
getter: true
constructor: false
beanPropertyField:
field: true
method: true
getter: true
constructor: true
beanPropertyGetter:
field: false
method: true
getter: false
constructor: true
beanPropertyBeanGetter:
field: false
method: false
getter: true
constructor: false