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?

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 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.


¿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.


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

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


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.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.