как вызвать суперконструктор в Ломбоке

118

У меня есть класс

@Value
@NonFinal
public class A {
    int x;
    int y;
}

У меня другой класс Б

@Value
public class B extends A {
    int z;
}

lombok выдает ошибку, говоря, что он не может найти конструктор A (), явно называть его то, что я хочу, чтобы lombok сделал, это дать аннотацию к классу b, чтобы он генерировал следующий код:

public class B extends A {
    int z;
    public B( int x, int y, int z) {
        super( x , y );
        this.z = z;
    }
}

У нас есть аннотация для этого на Ломбоке?

user2067797
источник

Ответы:

169

На Ломбоке это невозможно. Хотя это была бы действительно хорошая функция, она требует разрешения, чтобы найти конструкторы суперкласса. Суперкласс известен только по имени в момент вызова Ломбока. Использование операторов импорта и пути к классам для поиска фактического класса нетривиально. А во время компиляции нельзя просто использовать отражение для получения списка конструкторов.

Это не совсем невозможно , но результаты , используя разрешение в valи @ExtensionMethodучат нас , что это трудно и подверженным ошибкам.

Раскрытие информации: я разработчик Lombok.

Рул Спилкер
источник
@ roel-spilker Мы понимаем стоящие за этим сложности. Но может ли Lombok предоставить inConstructorметод для аннотаций конструктора, в котором мы можем указать, какой конструктор superLombok должен внедрить в сгенерированный конструктор?
Ману Манджунатх
1
afterConstructor тоже было бы неплохо для автоматической инициализации
Павел
@ Manu / @ Pawel: см. Запрос на улучшение ломбока: github.com/peichhorn/lombok-pg/issues/78 (в настоящее время открыто)
JJ Zabkar
Поскольку @Builder находится в официальном выпуске, см .: github.com/rzwitserloot/lombok/issues/853
Себастьян
4
По-прежнему невозможно?
FearX
21

Выпуск № 78 Lombok ссылается на эту страницу https://www.donneo.de/2015/09/16/lomboks-builder-annotation-and-inheritance/ с этим прекрасным объяснением:

@AllArgsConstructor 
public class Parent {   
     private String a; 
}

public class Child extends Parent {
  private String b;

  @Builder
  public Child(String a, String b){
    super(a);
    this.b = b;   
  } 
} 

В результате вы можете использовать сгенерированный конструктор следующим образом:

Child.builder().a("testA").b("testB").build(); 

Официальной документации объясняет это, но это явно не указывает на то , что вы можете облегчить его таким образом.

Я также обнаружил, что это хорошо работает с Spring Data JPA.

Джей Джей Забкар
источник
Можете ли вы привести пример использования этого с Spring Data JPA?
Marc Zampetti 02
30
Это вообще не отвечает на вопрос. Вместо этого он выполняет работу вручную, а вопрос заключался в том, как ее создать. В то же время это еще больше сбивает с толку, перетаскивая @Builder, что не имеет никакого отношения к вопросу.
Джаспер
8
На самом деле это очень полезно для тех, кто просто хочет создать структуру наследования, а затем использовать построитель. На 99% я все равно использую #lombok. Иногда нам просто нужно создавать вещи вручную, чтобы они работали так, как мы хотим. Так что спасибо @ jj-zabkar
Prince
но потом; просто закодируйте конструктор self + parent arg. Строитель не
нужен
Он будет работать в STS и eclipse, но когда вы создадите JAR-файл своего приложения, скорее всего, он потерпит неудачу. Я пробовал Оба SuperBuilder, Builder для наследования. Оба потерпели неудачу. Быть осторожен !!
P Satish Patro
6

Lombok не поддерживает то, что также указывается при создании любого @Valueаннотированного класса final(как вы знаете, используя @NonFinal).

Единственный обходной путь, который я нашел, - это объявить всех участников final самостоятельно и @Dataвместо этого использовать аннотацию. Эти подклассы должны быть аннотированы @EqualsAndHashCodeи нуждаются в явном конструкторе всех аргументов, поскольку Lombok не знает, как создать его, используя все аргументы одного суперкласса:

@Data
public class A {
    private final int x;
    private final int y;
}

@Data
@EqualsAndHashCode(callSuper = true)
public class B extends A {
    private final int z;

    public B(int x, int y, int z) {
        super(x, y);
        this.z = z;
    }
}

В частности, конструкторы подклассов делают решение немного неопрятным для суперклассов с большим количеством членов, извините.

Арне Бурмейстер
источник
1
Не могли бы вы подробнее объяснить, почему «подклассы должны быть аннотированы @EqualsAndHashCode»? Разве эта аннотация не включена @Data? Thx :)
Джерард Бош
1
@GerardB @Dataтакже создает equals () и hashCode (), но не заботится о наследовании. Чтобы убедиться, что используется суперкласс equals () и hashCode (), вам нужна явная генерация с помощью callSuper
Арне Бурмейстер,
5

для суперклассов с большим количеством членов я бы посоветовал вам использовать @Delegate

@Data
public class A {
    @Delegate public class AInner{
        private final int x;
        private final int y;
    }
}

@Data
@EqualsAndHashCode(callSuper = true)
public class B extends A {
    private final int z;

    public B(A.AInner a, int z) {
        super(a);
        this.z = z;
    }
}
Крис
источник
Это интересный подход, нравится!
Arne Burmeister
@Delegateесть @Target({ElementType.FIELD, ElementType.METHOD}). AInner должно быть поле в A.
boriselec
3

Если у дочернего класса больше членов, чем у родительского, это можно сделать не очень чисто, но коротко:

@Data
@RequiredArgsConstructor
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class User extends BaseEntity {
    private @NonNull String fullName;
    private @NonNull String email;
    ... 

    public User(Integer id, String fullName, String email, ....) {
        this(fullName, email, ....);
        this.id = id;
    }
}

@Data
@AllArgsConstructor
abstract public class BaseEntity {
   protected Integer id;

   public boolean isNew() {
      return id == null;
   }
}
Григорий Кислин
источник
3

Версия 1.18 Lombok представила аннотацию @SuperBuilder. Мы можем использовать это, чтобы решить нашу проблему более простым способом.

Вы можете обратиться к https://www.baeldung.com/lombok-builder-inheritance#lombok-builder-and-inheritance-3 .

поэтому в вашем дочернем классе вам понадобятся эти аннотации:

@Data
@SuperBuilder
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)

в вашем родительском классе:

@Data
@SuperBuilder
@NoArgsConstructor
Алан Хо
источник
0

Как вариант, вы можете использовать com.fasterxml.jackson.databind.ObjectMapperдля инициализации дочернего класса от родительского

public class A {
    int x;
    int y;
}

public class B extends A {
    int z;
}

ObjectMapper MAPPER = new ObjectMapper(); //it's configurable
MAPPER.configure( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false );
MAPPER.configure( SerializationFeature.FAIL_ON_EMPTY_BEANS, false );

//Then wherever you need to initialize child from parent:
A parent = new A(x, y);
B child = MAPPER.convertValue( parent, B.class);
child.setZ(z);

Вы все еще можете использовать любые lombokаннотации для A и B, если вам нужно.

ITisha
источник