Kotlin data classes and annotations go really well together, but it is easy to mess it up.

Annotations can be used on data classes, but their behaviour might seem a bit weird at first. For example, if you are using the Spring Validation framework to validate your beans your code might look a bit like this:

fun foo(@Valid @RequestBody bar : Foo){

data class Foo(@Pattern(regexp = "^\\d+\$", message = "Housenumber should only contain numbers") val houseNr: String))

This looks good at first, but after testing the validations you will find out it will not work. This is due to the fact that the annotation is actually in the wrong spot. This can easily be explained by reading the kotlin documentation:

"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. "

If we look at the @Pattern annotation we can see that its target is @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE }). Since param is applicable it will be added to the constructor param. The Spring validation framework however wants the annotation to be on the field or the getter.

Luckily there is an easy fix for this; Kotlin allows us to specify the location of the annotations. So in this case we should either tell it to use the field or the getter. We can do this by using the following notation: @<location>:Annotation. To give an example:

data class Foo(@get:Pattern(regexp = "^\\d+\$", message = "...") val houseNr: String)) //getter
data class Foo(@field:Pattern(regexp = "^\\d+\$", message = "...") val houseNr: String)) //field

Now that the annotations are in the right place your validations will work again :)

Compiled and tested with Kotlin version 1.4.10.