comment appeler un super constructeur à Lombok

118

j'ai un cours

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

J'ai une autre classe B

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

lombok lance une erreur disant qu'il ne trouve pas le constructeur A (), appelez-le explicitement ce que je veux que lombok fasse est de donner une annotation à la classe b de telle sorte qu'elle génère le code suivant:

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

Avons-nous une annotation pour faire cela à Lombok?

user2067797
la source

Réponses:

169

Ce n'est pas possible à Lombok. Bien que ce soit une fonctionnalité vraiment intéressante, il faut une résolution pour trouver les constructeurs de la super classe. La super classe n'est connue que par son nom au moment où Lombok est invoqué. L'utilisation des instructions d'importation et du chemin d'accès aux classes pour trouver la classe réelle n'est pas anodine. Et pendant la compilation, vous ne pouvez pas simplement utiliser la réflexion pour obtenir une liste de constructeurs.

Ce n'est pas tout à fait impossible, mais les résultats en utilisant la résolution valet @ExtensionMethodnous ont appris que c'est difficile et sujet aux erreurs.

Divulgation: Je suis un développeur Lombok.

Roel Spilker
la source
@ roel-spilker Nous comprenons les complexités derrière cela. Mais Lombok peut-il fournir une inConstructorméthode pour les annotations de constructeur où nous pouvons spécifier quel constructeur de superLombok injectera dans le constructeur généré?
Manu Manjunath
1
afterConstructor serait également bien de faire une initialisation automatique
Pawel
@ Manu / @ Pawel: voir la demande d'amélioration de lombok: github.com/peichhorn/lombok-pg/issues/78 (actuellement ouvert)
JJ Zabkar
Puisque @Builder est en version officielle, voir: github.com/rzwitserloot/lombok/issues/853
Sebastian
4
Pas encore possible?
FearX
22

Le numéro 78 de Lombok fait référence à cette page https://www.donneo.de/2015/09/16/lomboks-builder-annotation-and-inheritance/ avec cette belle explication:

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

En conséquence, vous pouvez ensuite utiliser le générateur généré comme ceci:

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

La documentation officielle l' explique, mais elle n'indique pas explicitement que vous pouvez le faciliter de cette manière.

J'ai également trouvé que cela fonctionne bien avec Spring Data JPA.

JJ Zabkar
la source
Pouvez-vous donner un exemple de cette utilisation avec Spring Data JPA?
Marc Zampetti
29
Cela ne répond pas du tout à la question. Au lieu de cela, il fait le travail à la main, alors que la question était de savoir comment le générer. En même temps, cela rend le tout plus déroutant en faisant glisser @Builder, ce qui n'a rien à voir avec la question.
Jasper
8
En fait, c'est très utile pour ceux qui veulent simplement créer une structure d'héritage et ensuite utiliser le générateur. C'est à 99% la raison pour laquelle j'utilise #lombok de toute façon. Parfois, nous devons juste fabriquer des objets à la main pour que cela fonctionne comme nous le souhaitons, alors merci @ jj-zabkar
Babajide Prince
mais alors; codez simplement le constructeur arg self + parent. Pas besoin d'utiliser un constructeur
Juh_
Cela fonctionnera dans STS et eclipse, mais lorsque vous générez le fichier JAR de votre application, il échouera très probablement. J'ai essayé Both SuperBuilder, Builder pour l'héritage. Les deux ont échoué. Faites attention !!
P Satish Patro
6

Lombok ne prend pas en charge cela également indiqué en créant une @Valueclasse annotée final(comme vous le savez en utilisant @NonFinal).

La seule solution de contournement que j'ai trouvée est de déclarer tous les membres définitifs vous-même et d'utiliser l' @Dataannotation à la place. Ces sous-classes doivent être annotées par @EqualsAndHashCodeet ont besoin d'un constructeur explicite all args car Lombok ne sait pas comment en créer une en utilisant la classe all args de la 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;
    }
}

Surtout les constructeurs des sous-classes rendent la solution un peu désordonnée pour les superclasses avec de nombreux membres, désolé.

Arne Burmeister
la source
1
Pourriez-vous expliquer un peu plus pourquoi «les sous-classes doivent être annotées par @EqualsAndHashCode»? Cette annotation n'est-elle pas incluse par @Data? Thx :)
Gerard Bosch
1
@GerardB @Datacrée également equals () et hashCode () mais ne se soucie d'aucun héritage. Pour vous assurer que la superclasse equals () et hashCode () est utilisée, vous avez besoin de la génération explicite avec callSuper
Arne Burmeister
5

pour les superclasses avec de nombreux membres, je vous suggère d'utiliser @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;
    }
}
Kris
la source
C'est une approche intéressante, comme ça!
Arne Burmeister
@Delegateest @Target({ElementType.FIELD, ElementType.METHOD}). AInner devrait être sur le terrain A.
boriselec le
3

Si la classe enfant a plus de membres que le parent, cela pourrait être fait pas très propre, mais de manière courte:

@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;
   }
}
Grigory Kislin
la source
3

La version 1.18 de Lombok a introduit l'annotation @SuperBuilder. Nous pouvons l'utiliser pour résoudre notre problème de manière plus simple.

Vous pouvez vous référer à https://www.baeldung.com/lombok-builder-inheritance#lombok-builder-and-inheritance-3 .

donc dans votre classe enfant, vous aurez besoin de ces annotations:

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

dans ta classe parentale:

@Data
@SuperBuilder
@NoArgsConstructor
Alan Ho
la source
0

En option, vous pouvez utiliser com.fasterxml.jackson.databind.ObjectMapperpour initialiser une classe enfant à partir du parent

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

Vous pouvez toujours utiliser des lombokannotations sur A et B si vous en avez besoin.

ITisha
la source