Можно ли в Java с помощью аннотаций убрать дублирования полей и сеттеров в статическом классе и его родительском классе?

В погоне за лаконичным, но при этом обязующим не забыть сделать необходимые действия синтаксисом реализовал билдер (паттерн для передачи большого количества параметров), позволяющий создавать объекты таким образом:

new Person(
  Person.specifyParameters(new Person.RequiredParameters() {
    @Override public void setID() { this.ID = "1"; }
    @Override public void setFamilyName() { this.familyName = "毛"; }
  })
    // и дальше опциональные параметры...
    .setGivenName("澤東")
    .setFamilyNameSpell("Мао")
    .setGivenNameSpell("Цзэдун")
);

Его преимущества:

  1. Последовательность параметров не имеет значения.
  2. Пользователь не может забыть, КАКИЕ параметры обязательны к инициализации, а какие - опциональные. Хотя, например, в setID() пользователь и может по невнимательности просеттить что-то вместо ID, он не может забыть про то, что надо реализовать метод setID - иначе программа не скомпилируется.

Ну и ещё, не знаю, считать это достоинством или нет, класс в классе Person инкапсулирован билдер для своих же инстансов. Объективно пока не могу доказать, что это плохо, но мне как-то не хочется выносить билдер в отдельный класс.

Недостаток реализации - дублирование полей и сеттеров в классе Person и в его дочернем классе ParametersObject. Была бы возможность сослаться на собственный статический абстрактный класс, работы было бы меньше, а пока реализация такова:

package dev.example.BusinessRules.Entities;


public class Person {

  private String ID;
  private String familyName;
  private String familyNameSpell;
  private String givenName;
  private String givenNameSpell;


  public Person(ParametersObject parametersObject) {
    parametersObject.initializeSpecifiedFields(this);
  }

  public static ParametersObject specifyParameters(Person.RequiredParameters requiredParameters) {
    return new Person.ParametersObject(requiredParameters);
  }


  public String getID() { return this.ID; }

  public String getFamilyName() {
    return familyName;
  }
  public Person setFamilyName(String familyName) {
    this.familyName = familyName;
    return this;
  }

  public String getGivenName() { return givenName; }
  public Person setGivenName(String givenName) {
    this.givenName = givenName;
    return this;
  }

  public String getFamilyNameSpell() { return familyNameSpell; }
  public Person setFamilyNameSpell(String familyNameSpell) {
    this.familyNameSpell = familyNameSpell;
    return this;
  }

  public String getGivenNameSpell() {
    return givenNameSpell;
  }
  public Person setGivenNameSpell(String givenNameSpell) {
    this.givenNameSpell = givenNameSpell;
    return this;
  }

  public static class ParametersObject {

    private final String ID;
    private final String familyName;
    private String givenName;
    private String familyNameSpell;
    private String givenNameSpell;

    public ParametersObject(Person.RequiredParameters requiredParameters) {
      this.ID = requiredParameters.ID;
      this.familyName = requiredParameters.familyName;
    }

    public void initializeSpecifiedFields(Person person) {
      person.ID = this.ID;
      person.familyName = this.familyName;
      person.givenName = this.givenName;
      person.familyNameSpell = this.familyNameSpell;
      person.givenNameSpell = this.givenNameSpell;
    }

    public ParametersObject setGivenName(String givenName) { this.givenName = givenName; return this; }
    public ParametersObject setFamilyNameSpell(String familyNameSpell) { this.familyNameSpell = familyNameSpell; return this; }
    public ParametersObject setGivenNameSpell(String givenNameSpell) { this.givenNameSpell = givenNameSpell; return this; }
  }

  public static abstract class RequiredParameters {

    public String ID;
    public String familyName;

    public abstract void setID();
    public abstract void setFamilyName();
  }
}

Прочитав уроки по Java-аннотациям, в большинстве случаев так и не понял, что они конкретно делают и вообще не понял, КАК они это делают. Но вопрос такой: можно ли внутри ParametersObject уменьшить количество кода с помощью аннотаций? Убрать нужно в первую очередь дублирования полей и геттеров.

Цель всей этой акробатики - приблизится к лаконичному TypeScript-синтаксису. Вот как просто на TypeScript-е можно реализовать приведённую выше концепцию:

class Person {

  private ID;
  private familyName;
  private familyNameSpell?; // опциональный параметр
  private givenName?; // опциональный параметр
  private givenNameSpell?; // опциональный параметр

  public constructor(parametersObject: {
    ID: string; // обязательный параметр - без него программа не скомпилируется
    familyName: string; // обязательный параметр - без него программа не скомпилируется
    familyNameSpell?: string; // опциональный параметр
    givenName?: string; // опциональный параметр
    givenNameSpell?: string;
  }) {
    this.ID = parametersObject.ID;
    this.familyName = parametersObject.familyName;
    this.familyNameSpell = parametersObject.familyNameSpell;
    this.givenName = parametersObject.givenName;
    this.givenNameSpell = parametersObject.givenNameSpell;
  }
}

new Person({
  ID: "1",
  familyName: "毛", // если забыть обязательный параметр - TypeScript не скомпилируется
  familyNameSpell : "Мао",
  // ... опустил остальные опциональные параметры
})

Вторая реализация

Поразмыслив, я пришел к выводу, что в ParametersObject указывать опциональные параметры не нужно - мы можем их настроить уже в самом инстансе Person через цепные сеттеры. После этого оказалось, что класс ParametersObject не нужен - нужен только RequiredParameters.

public class Person {

  private String ID;
  private String familyName;
  private String familyNameSpell;
  private String givenName;
  private String givenNameSpell;


  public Person(RequiredParameters parametersObject) {
    this.ID = parametersObject.ID;
    this.familyName = parametersObject.familyName;
  }


  public String getID() { return this.ID; }

  public String getFamilyName() {
    return familyName;
  }
  public Person setFamilyName(String familyName) {
    this.familyName = familyName;
    return this;
  }

  public String getGivenName() { return givenName; }
  public Person setGivenName(String givenName) {
    this.givenName = givenName;
    return this;
  }

  public String getFamilyNameSpell() { return familyNameSpell; }
  public Person setFamilyNameSpell(String familyNameSpell) {
    this.familyNameSpell = familyNameSpell;
    return this;
  }

  public String getGivenNameSpell() {
    return givenNameSpell;
  }
  public Person setGivenNameSpell(String givenNameSpell) {
    this.givenNameSpell = givenNameSpell;
    return this;
  }

  public static abstract class RequiredParameters {

    public String ID;
    public String familyName;

    public abstract void setID();
    public abstract void setFamilyName();
  }
}

Теперь API стала немного лаконичнее:

new Person(
  new Person.RequiredParameters() {
    public void setID() { this.ID = "1"; }
    public void setFamilyName() { this.familyName = "毛"; }
  }
)
    .setGivenName("澤東")
    .setFamilyNameSpell("Мао")
    .setGivenNameSpell("Цзэдун")

Ответы (0 шт):