jak nazwać super konstruktora w Lombok


118

mam klasę

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

Mam inną klasę B.

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

lombok generuje błąd, mówiąc, że nie można znaleźć konstruktora A (), jawnie nazwij to tym, co chcę, aby lombok zrobił, to przekazanie adnotacji do klasy b, tak aby generowała następujący kod:

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

Czy mamy adnotację, aby to zrobić w Lombok?

Odpowiedzi:


169

W Lombok nie jest to możliwe. Chociaż byłaby to naprawdę fajna funkcja, wymaga rozdzielczości, aby znaleźć konstruktorów super klasy. Superklasa jest znana z nazwy dopiero w momencie wywołania Lomboka. Użycie instrukcji importu i ścieżki klasy do znalezienia właściwej klasy nie jest trywialne. A podczas kompilacji nie możesz po prostu użyć refleksji, aby uzyskać listę konstruktorów.

Nie jest to całkowicie niemożliwe, ale wyniki przy użyciu rozdzielczości w vali @ExtensionMethodnauczyły nas, że jest to trudne i podatne na błędy.

Ujawnienie: jestem programistą Lombok.


@ roel-spilker Rozumiemy zawiłości za tym. Ale czy Lombok może zapewnić inConstructormetodę adnotacji konstruktorów, w której możemy określić, który konstruktor superma wstrzyknąć Lombok do wygenerowanego konstruktora?
Manu Manjunath

1
afterConstructor również byłby miły do ​​wykonania automatycznej inicjalizacji
Paweł

@ Manu / @ Pawel: patrz prośba o ulepszenie lombok: github.com/peichhorn/lombok-pg/issues/78 (obecnie otwarte)
JJ Zabkar

Ponieważ @Builder jest w oficjalnym wydaniu, patrz: github.com/rzwitserloot/lombok/issues/853
Sebastian

4
Nadal nie jest możliwe?
FearX

22

Wydanie Lombok # 78 odwołuje się do tej strony https://www.donneo.de/2015/09/16/lomboks-builder-annotation-and-inheritance/ z tym uroczym wyjaśnieniem:

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

W rezultacie możesz użyć wygenerowanego kreatora w następujący sposób:

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

Oficjalna dokumentacja wyjaśnia to, ale nie wyraźnie podkreślić, że można ułatwić go w ten sposób.

Zauważyłem również, że działa to dobrze z Spring Data JPA.


Czy możesz podać przykład wykorzystania tego w Spring Data JPA?
Marc Zampetti

30
To wcale nie odpowiada na pytanie. Zamiast tego wykonuje pracę ręcznie, podczas gdy pytanie brzmiało, jak go wygenerować. Jednocześnie sprawia, że ​​całość jest bardziej zagmatwana, przeciągając @Builder, który nie ma nic wspólnego z pytaniem.
Jasper

8
W rzeczywistości jest to bardzo przydatne dla tych, którzy chcą po prostu utworzyć strukturę dziedziczenia, a następnie użyć kreatora. To jest 99% powód, dla którego i tak używam #lombok. Czasami musimy po prostu zrobić coś ręcznie, aby działało tak, jak chcemy. Więc dzięki @ jj-zabkar
Babajide Prince

ale wtedy; po prostu zakoduj konstruktor argumentów self + parent. Nie ma potrzeby używania konstruktora
Juh_

Będzie działać w STS i eclipse, ale kiedy wygenerujesz plik JAR swojej aplikacji, najprawdopodobniej się nie powiedzie. Wypróbowałem zarówno SuperBuilder, Builder do dziedziczenia. Obie zawiodły. Bądź ostrożny !!
P Satish Patro

6

Lombok nie obsługuje tego również wskazanego przez tworzenie @Valueklas z adnotacjami final(jak wiesz, używając @NonFinal).

Jedynym rozwiązaniem, które znalazłem, jest samodzielne zadeklarowanie wszystkich członków jako ostatecznych i użycie @Datazamiast tego adnotacji. Te podklasy muszą być opatrzone adnotacjami @EqualsAndHashCodei wymagają jawnego konstruktora wszystkich argumentów, ponieważ Lombok nie wie, jak utworzyć klasę za pomocą wszystkich argumentów jednej z superklasy:

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

Szczególnie konstruktorzy podklas sprawiają, że rozwiązanie jest trochę nieporządne dla nadklas z wieloma członkami, przepraszam.


1
Czy mógłbyś wyjaśnić trochę więcej, dlaczego „podklasy muszą być opatrzone adnotacją @EqualsAndHashCode”? Czy ta adnotacja nie jest uwzględniona przez @Data? Dzięki :)
Gerard Bosch,

1
@GerardB @Datarównież tworzy equals () i hashCode (), ale nie dba o żadne dziedziczenie. Aby upewnić się, że nadklasa jest równa () i hashCode () jest używana, potrzebujesz jawnej generacji z callSuper
Arne Burmeister

5

w przypadku superklas z wieloma członkami sugerowałbym użycie @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;
    }
}

To bardzo ciekawe podejście!
Arne Burmeister

@Delegatejest @Target({ElementType.FIELD, ElementType.METHOD}). AInner powinno być w polu A.
boriselec

3

Jeśli klasa dziecięca ma więcej członków niż rodzica, można to zrobić niezbyt czysto, ale krótko:

@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

Jako opcję możesz użyć com.fasterxml.jackson.databind.ObjectMapperdo zainicjowania klasy potomnej od rodzica

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

W lombokrazie potrzeby możesz nadal używać adnotacji w punktach A i B.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.