Suggestion for parameterless data class


#1

I was trying spring’s form input validation with kotlin. If I created PersonForm using data class like this:

data class PersonForm (
    @Size(min=2, max=30)
    @NotBlank
    var name: String = "",
    //...
)  {
    constructor(): this("") { }
}

the validation doesn’t work, the form was just passed successfully. Adding secondary empty constructor doesn’t work too.

The only solution is to use plain class:

class PersonForm {
    @Size(min=2, max=30)
    @NotBlank
    var name: String = ""
    //...
}

but I would lose the benefits of data class.

It seems the validator expects the form class must have primary empty constructor.

So I would like to suggest if we can have parameterless data class? e.g.

data class PersonForm {
    @Size(min=2, max=30)
    @NotBlank
    var name: String = ""
    //...
}

If I’m not wrong, data class is almost similar to case class in scala but scala case class can have empty parameter.


#2

A data class uses properties defined in the primary constructor to generate its equals, hashCode, toString and other methods. A data class declared in the way that you suggest would have meaningless implementations of those methods, which is very unlikely to be what you actually want.


#3

It’s unlikely that the validator could know which of constructors is primary.

Try to inspect if there’s a difference in the generated bytecode in how the name property is annotated in case of data class and plain class.


#4

Alright, I checked them with JD-GUI, here what I found out for the generated name field:

plain class:

public final class PersonForm
{
    @Size(min=2, max=30)
    @NotBlank
    @NotNull
    private String name = "";

    ...
}

data class:

public final class PersonForm
{
    @NotNull
    private String name;

    public PersonForm(@NotNull @Size(min=2, max=30) @NotBlank String name, ...)
    {
      this.name = name;
      ...
    }

    ...
}

So on the data class, the @Size and @NotBlank annotation aren’t put correctly on the private String name field declaration, instead they are put on the constructor’s parameters.

Not related with the above issue, just curious that there are these methods generated for each constructor fields on data class:

  @NotNull
  public final String component1()
  {
    return this.name;
  }

  @NotNull
  public final Integer component2()
  {
    return this.age;
  }

What are they used for?


#5

You can use @field:Name syntax to ensure that the annotation is applied to the field and not the constructor parameter: http://kotlinlang.org/docs/reference/annotations.html#annotation-use-site-targets

componentN() functions are used for destructuring declarations: http://kotlinlang.org/docs/reference/multi-declarations.html


#6

I’ve tried adding @field:Name and then @field:name, but they gave compile error. I think you meant @field:<annotation_name>, thus:

data class PersonForm (
    @field:Size(min=2, max=30)
    @field:NotBlank
    var name: String = "",

It took me awhile to figure out that. The documentation is a bit confusing too, it mentioned @field:Ann but don’t describe what is Ann at that section. It is mentioned at above which requires some reading. I was thinking I have to add new annotation @field:??, but turns out it’s just prefixing @field to existing annotation.

Suggestion:
I think it’s better if kotlin compiler put the annotation into field location by default if the constructor param is val or var in order to avoid the overhead of @field prefix, as most people would expect they are annotated on field location.


#7

You could improve the documentation, it’s on GitHub. That would be great!