як зателефонувати супер конструктору в Ломбок


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;
    }
}

Чи є у нас примітка про це в Ломбоку?

Відповіді:


169

Це неможливо в Ломбоку. Хоча це було б дійсно приємною особливістю, вона вимагає дозволу, щоб знайти конструкторів суперкласу. Суперклас відомий лише по імені, коли Ломбок викликається. Використання операторів імпорту та classpath для пошуку фактичного класу не є тривіальним. А під час компіляції ви не можете просто використати рефлексію, щоб отримати список конструкторів.

Це не зовсім неможливо , але результати , використовуючи дозвіл в valі @ExtensionMethodвчать нас , що це важко і схильним до помилок.

Розкриття: Я - розробник Lombok.


@ roel-spilker Ми розуміємо, що стоять за цим завданням. Але чи може Ломбок надати inConstructorметод для анотацій конструктора, де ми можемо вказати, який конструктор superLombok введе в створений конструктор?
Ману Манджунат

1
afterConstructor також було б непогано зробити автоматичну ініціалізацію
Pawel

@ Manu / @ Pawel: див. Запит на покращення lombok: github.com/peichhorn/lombok-pg/isissue/78 (наразі відкрито)
JJ Zabkar

Оскільки @Builder знаходиться в офіційному релізі, дивіться: github.com/rzwitserloot/lombok/isissue/853
Себастьян

4
Ще не можливо?
FearX

22

Lombok Issue № 78 посилається на цю сторінку 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?
Марк Зампетті

29
Це зовсім не відповідає на питання. Натомість це робить роботу вручну, тоді як питання полягало в тому, як її генерувати. У той же час це робить все більш заплутаним, перетягуючи @Builder, що не має нічого спільного з питанням.
Джаспер

8
Насправді це дуже корисно для тих, хто просто хоче створити спадкову структуру, а потім використовувати builder. Це 99% причин, що я будь-коли використовую #lombok. Іноді нам просто доводиться обробляти речі, щоб це працювало так, як ми забажали. Тож дякуємо @ jj-zabkar
Принц Бабаїде

але з іншого боку; просто кодуйте конструктор аргументів self +. Немає необхідності використовувати конструктор
Juh_

Він буде працювати в STS & eclipse, але коли ви генеруєте файл JAR програми, швидше за все, це не вдасться. Я спробував обидва SuperBuilder, Builder для спадкування. Обидва провалилися. Будь обережний !!
P

6

Lombok не підтримує те, що також вказано, роблячи будь-який @Valueанотований клас final(як ви знаєте, використовуючи @NonFinal).

Єдине рішення, яке я знайшов, - оголосити всіх членів остаточним і використовувати @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також створює рівняння () та hashCode (), але не піклується про будь-яку спадщину. Щоб переконатися, що суперклас дорівнює () та хеш-коду (), вам потрібно чітке покоління з callSuper
Arne Burmeister

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;
    }
}

Це цікавий підхід, подобається!
Арн Бурмейстер

@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, якщо вам потрібно.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.