come chiamare un super costruttore a Lombok


118

Ho una lezione

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

Ho un'altra classe B

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

lombok sta generando un errore dicendo che non riesce a trovare il costruttore A (), chiamalo esplicitamente ciò che voglio che lombok faccia è dare annotazioni alla classe b in modo tale che generi il seguente codice:

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

Abbiamo un'annotazione per farlo a Lombok?

Risposte:


169

Questo non è possibile a Lombok. Anche se sarebbe una caratteristica davvero carina, richiede risoluzione per trovare i costruttori della super classe. La super classe è conosciuta solo per nome nel momento in cui viene invocato Lombok. L'utilizzo delle istruzioni import e del classpath per trovare la classe effettiva non è banale. E durante la compilazione non puoi semplicemente usare la reflection per ottenere un elenco di costruttori.

Non è del tutto impossibile, ma i risultati che utilizzano la risoluzione vale ci @ExtensionMethodhanno insegnato che è difficile e soggetto a errori.

Divulgazione: sono uno sviluppatore Lombok.


@ roel-spilker Comprendiamo le complessità che stanno dietro. Ma Lombok può fornire un inConstructormetodo per le annotazioni del costruttore in cui possiamo specificare quale costruttore di superLombok deve iniettare nel costruttore generato?
Manu Manjunath

1
afterConstructor sarebbe anche carino per fare un po 'di inizializzazione automatica
Pawel

@ Manu / @ Pawel: vedi la richiesta di miglioramento di lombok: github.com/peichhorn/lombok-pg/issues/78 (attualmente aperto)
JJ Zabkar

Dato che @Builder è in versione ufficiale, vedere: github.com/rzwitserloot/lombok/issues/853
Sebastian

4
Non è ancora possibile?
FearX

22

Il numero 78 di Lombok fa riferimento a questa pagina https://www.donneo.de/2015/09/16/lomboks-builder-annotation-and-inheritance/ con questa bella spiegazione:

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

Di conseguenza puoi quindi utilizzare il builder generato in questo modo:

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

La documentazione ufficiale lo spiega, ma non indica esplicitamente che puoi agevolarlo in questo modo.

Ho anche scoperto che funziona bene con Spring Data JPA.


Potete fornire un esempio di questo utilizzo con Spring Data JPA?
Marc Zampetti

29
Questo non risponde affatto alla domanda. Invece, fa il lavoro a mano, mentre la domanda era come generarlo. Allo stesso tempo, rende il tutto più confuso trascinando @Builder, che non ha nulla a che fare con la domanda.
Jasper

8
In realtà questo è molto utile per coloro che vogliono semplicemente creare una struttura di ereditarietà e quindi utilizzare builder. Questo è il 99% dei motivi per cui uso comunque #lombok. A volte dobbiamo solo creare oggetti artigianali per farlo funzionare come desideriamo, quindi grazie @ jj-zabkar
Babajide Prince

ma allora; codifica semplicemente il costruttore self + parent arg. Non c'è bisogno di usare un costruttore
Juh_

Funzionerà in STS ed eclipse, ma quando generi il file JAR della tua applicazione, molto probabilmente fallirà. Ho provato Both SuperBuilder, Builder per l'ereditarietà. Entrambi hanno fallito. Stai attento !!
P Satish Patro

6

Lombok non supporta anche quella indicata facendo alcuna @Valueclasse annotata final(come saprai usando @NonFinal).

L'unica soluzione alternativa che ho trovato è dichiarare tutti i membri finali da soli e utilizzare @Datainvece l' annotazione. Quelle sottoclassi devono essere annotate @EqualsAndHashCodee necessitano di un costruttore esplicito di tutti gli argomenti poiché Lombok non sa come crearne uno utilizzando tutti gli argomenti della super classe:

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

Soprattutto i costruttori delle sottoclassi rendono la soluzione un po 'disordinata per le superclassi con molti membri, mi dispiace.


1
Potresti spiegare un po 'di più perché "le sottoclassi devono essere annotate da @EqualsAndHashCode"? Questa annotazione non è inclusa da @Data? Grazie :)
Gerard Bosch

1
@GerardB @Datacrea anche equals () e hashCode () ma non si preoccupa dell'ereditarietà. Per assicurarti che la superclasse sia uguale a () e hashCode () è necessaria la generazione esplicita con callSuper
Arne Burmeister

5

per le superclassi con molti membri ti consiglio di usare @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;
    }
}

Questo è un approccio interessante, piace!
Arne Burmeister

@Delegateè @Target({ElementType.FIELD, ElementType.METHOD}). AInner dovrebbe essere campo in A.
boriselec

3

Se la classe figlia ha più membri rispetto al genitore, potrebbe essere fatto in modo non molto pulito, ma breve:

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


0

Come opzione puoi usare com.fasterxml.jackson.databind.ObjectMapperper inizializzare una classe figlia da genitore

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

Puoi comunque utilizzare qualsiasi lombokannotazione su A e B se necessario.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.