como chamar o superconstrutor no Lombok


118

Eu tenho uma aula

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

Eu tenho outra classe B

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

lombok está lançando um erro dizendo que não foi possível encontrar o construtor A (), chame-o explicitamente o que eu quero que o lombok faça é dar uma anotação à classe b de modo que ela gere o seguinte código:

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

Temos uma anotação para fazer isso no Lombok?

Respostas:


169

Isso não é possível em Lombok. Embora seja um recurso muito bom, requer resolução para encontrar os construtores da superclasse. A superclasse só é conhecida pelo nome no momento em que o Lombok é chamado. Usar as instruções de importação e o caminho de classe para encontrar a classe real não é trivial. E durante a compilação você não pode simplesmente usar a reflexão para obter uma lista de construtores.

Não é totalmente impossível, mas os resultados usando a resolução em vale @ExtensionMethodnos ensinaram que é difícil e sujeito a erros.

Divulgação: eu sou um desenvolvedor do Lombok.


@ roel-spilker Compreendemos as complexidades por trás disso. Mas o Lombok pode fornecer um inConstructormétodo para anotações do construtor em que possamos especificar qual construtor do superLombok injetará no construtor gerado?
Manu Manjunath de

1
afterConstructor também seria bom para fazer alguma inicialização automática
Pawel

@ Manu / @ Pawel: veja a solicitação de aprimoramento do lombok: github.com/peichhorn/lombok-pg/issues/78 (atualmente aberto)
JJ Zabkar

Como @Builder está em lançamento oficial, consulte: github.com/rzwitserloot/lombok/issues/853
Sebastian

4
Ainda não é possível?
FearX

22

A edição nº 78 do Lombok faz referência a esta página https://www.donneo.de/2015/09/16/lomboks-builder-annotation-and-inheritance/ com esta bela explicação:

@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, você pode usar o construtor gerado desta forma:

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

A documentação oficial explica isso, mas não indica explicitamente que você pode facilitar dessa forma.

Eu também descobri que isso funciona bem com Spring Data JPA.


Você pode fornecer um exemplo disso sendo usado com Spring Data JPA?
Marc Zampetti

30
Isso não responde à pergunta de forma alguma. Em vez disso, ele faz o trabalho manualmente, enquanto a questão era como gerá-lo. Ao mesmo tempo, isso torna tudo mais confuso arrastando @Builder, que não tem nada a ver com a pergunta.
Jasper

8
Na verdade, isso é muito útil para aqueles que desejam apenas criar uma estrutura de herança e então usar o builder. Este é 99% do motivo pelo qual eu uso #lombok de qualquer maneira. Às vezes, só precisamos fazer coisas à mão para que funcionem da maneira que desejamos. Obrigado @ jj-zabkar
Babajide Prince

mas então; simplesmente codifique o construtor self + parent arg. Não há necessidade de usar um construtor
Juh_

Isso funcionará em STS e eclipse, mas quando você gerar o arquivo JAR do seu aplicativo, provavelmente irá falhar. Eu tentei ambos SuperBuilder, Builder para herança. Ambos falharam. Seja cuidadoso !!
P Satish Patro

6

O Lombok não oferece suporte para isso também indicado ao criar qualquer @Valueclasse anotada final(como você sabe usando @NonFinal).

A única solução alternativa que encontrei é declarar você mesmo todos os membros finais e usar a @Dataanotação. Essas subclasses precisam ser anotadas @EqualsAndHashCodee precisam de um construtor all args explícito, pois o Lombok não sabe como criar um usando todos os args da superclasse:

@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 os construtores das subclasses tornam a solução um pouco desordenada para superclasses com muitos membros, desculpe.


1
Você poderia explicar um pouco mais por que "subclasses precisam ser anotadas por @EqualsAndHashCode"? Esta anotação não foi incluída por @Data? Thx :)
Gerard Bosch

1
@GerardB @Datatambém cria equals () e hashCode (), mas não se preocupa com nenhuma herança. Para garantir que a superclasse equals () e hashCode () seja usada, você precisa da geração explícita com callSuper
Arne Burmeister

5

para superclasses com muitos membros, sugiro que você 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;
    }
}

Esta é uma abordagem interessante, goste!
Arne Burmeister

@Delegateé @Target({ElementType.FIELD, ElementType.METHOD}). AInner deve estar em campo A.
Boriselec

3

Se a classe filha tiver mais membros do que os pais, isso poderia ser feito de forma não muito limpa, mas de forma resumida:

@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 opção, você pode usar com.fasterxml.jackson.databind.ObjectMapperpara inicializar uma classe filha do pai

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

Você ainda pode usar qualquer lombokanotação em A e B se precisar.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.