cómo llamar al super constructor en Lombok

118

Tengo una clase

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

Tengo otra clase B

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

lombok arroja un error que dice que no puede encontrar el constructor A (), llámelo explícitamente, lo que quiero que lombok haga es dar una anotación a la clase b de modo que genere el siguiente código:

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

¿Tenemos una anotación para hacer eso en Lombok?

usuario2067797
fuente

Respuestas:

169

Esto no es posible en Lombok. Aunque sería una característica realmente agradable, requiere resolución para encontrar los constructores de la superclase. La superclase solo se conoce por su nombre en el momento en que se invoca Lombok. Usar las declaraciones de importación y la ruta de clases para encontrar la clase real no es trivial. Y durante la compilación, no puede usar la reflexión para obtener una lista de constructores.

No es del todo imposible, pero los resultados con resolución valy @ExtensionMethodnos han enseñado que es difícil y propenso a errores.

Divulgación: soy un desarrollador de Lombok.

Roel Spilker
fuente
@ roel-spilker Entendemos las complejidades detrás de esto. Pero, ¿puede Lombok proporcionar un inConstructormétodo para las anotaciones del constructor donde podamos especificar qué constructor superdebe inyectar Lombok en el constructor generado?
Manu Manjunath
1
afterConstructor también sería bueno para hacer una inicialización automática
Pawel
@ Manu / @ Pawel: ver la solicitud de mejora de lombok: github.com/peichhorn/lombok-pg/issues/78 (actualmente abierto)
JJ Zabkar
Dado que @Builder está en versión oficial, consulte: github.com/rzwitserloot/lombok/issues/853
Sebastian
4
¿Aún no es posible?
FearX
22

Lombok Issue # 78 hace referencia a esta página https://www.donneo.de/2015/09/16/lomboks-builder-annotation-and-inheritance/ con esta hermosa explicación:

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

Como resultado, puede usar el constructor generado de esta manera:

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

La documentación oficial explica esto, pero no señala explícitamente que puede facilitarlo de esta manera.

También encontré que esto funciona muy bien con Spring Data JPA.

JJ Zabkar
fuente
¿Puede proporcionar un ejemplo de cómo se usa con Spring Data JPA?
Marc Zampetti
29
Esto no responde a la pregunta en absoluto. En cambio, hace el trabajo a mano, mientras que la pregunta era cómo generarlo. Al mismo tiempo, hace que todo sea más confuso al arrastrar @Builder, que no tiene nada que ver con la pregunta.
Jasper
8
En realidad, esto es muy útil para aquellos que solo quieren crear una estructura de herencia y luego usar el constructor. Esta es la razón del 99% por la que uso #lombok de todos modos. A veces solo tenemos que hacer cosas a mano para que funcione de la manera que deseamos. Así que gracias @ jj-zabkar
Babajide Prince
pero entonces; simplemente codifique el constructor de arg self + parent. No es necesario utilizar un constructor
Juh_
Funcionará en STS y eclipse, pero cuando genere un archivo JAR de su aplicación, lo más probable es que falle. Probé Ambos SuperBuilder, Builder para heredar. Ambos fallaron. Ten cuidado !!
P Satish Patro
6

Lombok no admite lo que también se indica al hacer cualquier @Valueclase anotada final(como sabes al usar @NonFinal).

La única solución que encontré es declarar todos los miembros finales usted mismo y usar la @Dataanotación en su lugar. Esas subclases deben estar anotadas @EqualsAndHashCodey necesitan un constructor de todos los args explícito, ya que Lombok no sabe cómo crear uno usando todos los args uno de la superclase:

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

Especialmente los constructores de las subclases hacen que la solución sea un poco desordenada para superclases con muchos miembros, lo siento.

Arne Burmeister
fuente
1
¿Podría explicar un poco más por qué "las subclases deben ser anotadas por @EqualsAndHashCode"? ¿No está incluida esta anotación @Data? Thx :)
Gerard Bosch
1
@GerardB @Datatambién crea equals () y hashCode () pero no se preocupa por la herencia. Para asegurarse de que se use la superclase igual () y hashCode (), necesita la generación explícita con callSuper
Arne Burmeister
5

para superclases con muchos miembros, le sugiero que use @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
fuente
Este es un enfoque interesante, ¡me gusta!
Arne Burmeister
@Delegatees @Target({ElementType.FIELD, ElementType.METHOD}). AInner debe estar en el campo A.
boriselec
3

Si la clase secundaria tiene más miembros que los padres, se podría hacer de manera no muy limpia, pero corta:

@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
fuente
3

La versión 1.18 de Lombok introdujo la anotación @SuperBuilder. Podemos usar esto para resolver nuestro problema de una manera más sencilla.

Puede consultar https://www.baeldung.com/lombok-builder-inheritance#lombok-builder-and-inheritance-3 .

así que en la clase de su hijo, necesitará estas anotaciones:

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

en tu clase para padres:

@Data
@SuperBuilder
@NoArgsConstructor
Alan Ho
fuente
0

Como una opción que puede utilizar com.fasterxml.jackson.databind.ObjectMapperpara inicializar una clase secundaria de padre

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

Aún puede usar cualquier lombokanotación en A y B si lo necesita.

ITisha
fuente