cách gọi hàm tạo siêu trong Lombok


118

Tôi có một lớp học

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

Tôi có một lớp B khác

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

lombok đang gặp lỗi nói rằng nó không thể tìm thấy hàm tạo A (), gọi nó một cách rõ ràng là những gì tôi muốn lombok làm là đưa ra chú thích cho lớp b để nó tạo ra mã sau:

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

Chúng tôi có chú thích để làm điều đó trong Lombok không?

Câu trả lời:


169

Điều này là không thể ở Lombok. Mặc dù nó sẽ là một tính năng thực sự hay, nhưng nó đòi hỏi độ phân giải để tìm ra các hàm tạo của siêu lớp. Lớp học siêu cấp chỉ được biết đến với cái tên khi Lombok được triệu tập. Việc sử dụng các câu lệnh import và classpath để tìm lớp thực tế là không hề nhỏ. Và trong quá trình biên dịch, bạn không thể chỉ sử dụng phản chiếu để có được danh sách các hàm tạo.

Nó không phải là hoàn toàn bất khả thi nhưng kết quả sử dụng độ phân giải trong val@ExtensionMethodđã dạy cho chúng ta biết rằng là nó cứng và dễ bị lỗi.

Tiết lộ: Tôi là nhà phát triển Lombok.


@ roel-spilker Chúng tôi hiểu sự phức tạp đằng sau nó. Nhưng Lombok có thể cung cấp một inConstructorphương thức cho các chú thích của hàm tạo mà chúng ta có thể chỉ định hàm tạo nào của superLombok sẽ đưa vào hàm tạo được tạo không?
Manu Manjunath

1
afterConstructor cũng sẽ rất tuyệt khi thực hiện một số khởi tạo tự động
Pawel

@ Manu / @ Pawel: xem yêu cầu nâng cấp lombok: github.com/peichhorn/lombok-pg/issues/78 (hiện đang mở)
JJ Zabkar

Vì @Builder đang trong bản phát hành chính thức, hãy xem: github.com/rzwitserloot/lombok/issues/853
Sebastian

4
Vẫn không được?
FearX

22

Lombok Issue # 78 tham khảo trang này https://www.donneo.de/2015/09/16/lomboks-builder-annotation-and-inheritance/ với lời giải thích đáng yêu này:

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

Do đó, bạn có thể sử dụng trình tạo đã tạo như sau:

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

Các tài liệu chính thức giải thích điều này, nhưng nó không rõ ràng chỉ ra rằng bạn có thể tạo điều kiện cho nó theo cách này.

Tôi cũng thấy điều này hoạt động tốt với Spring Data JPA.


Bạn có thể cung cấp một ví dụ về điều này được sử dụng với Spring Data JPA không?
Marc Zampetti

29
Điều này không trả lời câu hỏi nào cả. Thay vào đó, nó thực hiện công việc bằng tay, trong khi câu hỏi là làm thế nào để tạo ra nó. Đồng thời, nó làm cho toàn bộ sự việc trở nên khó hiểu hơn bằng cách kéo @Builder, không liên quan gì đến câu hỏi.
Jasper

8
Trên thực tế, điều này rất hữu ích cho những người chỉ muốn tạo một cấu trúc kế thừa và sau đó sử dụng trình xây dựng. Đây là lý do 99% tôi sử dụng #lombok. Đôi khi chúng ta chỉ cần chất liệu thủ công để làm cho nó làm việc theo cách chúng tôi desire.So nhờ @ jj-zabkar
Babajide Hoàng tử

nhưng sau đó; chỉ cần viết mã phương thức khởi tạo đối số tự + cha. Không cần sử dụng một trình xây dựng
Juh_

Nó sẽ hoạt động trong STS & eclipse, nhưng khi bạn tạo tệp JAR của ứng dụng của mình, rất có thể nó sẽ không thành công. Tôi đã thử cả SuperBuilder, Builder để có tính kế thừa. Cả hai đều thất bại. Hãy cẩn thận !!
P Satish Patro

6

Lombok không hỗ trợ điều đó cũng được chỉ ra bằng cách tạo bất kỳ @Valuelớp chú thích nào final(như bạn biết bằng cách sử dụng @NonFinal).

Cách giải quyết duy nhất mà tôi tìm thấy là tự khai báo tất cả các thành viên và sử dụng @Datachú thích thay thế. Các lớp con đó cần được chú thích bởi @EqualsAndHashCodevà cần một phương thức khởi tạo tất cả các args rõ ràng vì Lombok không biết cách tạo một lớp bằng cách sử dụng tất cả các args một trong các siêu lớp:

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

Đặc biệt là hàm tạo của các lớp con làm cho giải pháp hơi rắc rối cho các lớp cha có nhiều thành viên, xin lỗi.


1
Bạn có thể giải thích thêm một chút tại sao "các lớp con cần được chú thích bởi @EqualsAndHashCode"? Không phải chú thích này được bao gồm bởi @Data? Thx :)
Gerard Bosch

1
@GerardB @Datacũng tạo ra equals () và hashCode () nhưng không quan tâm đến bất kỳ sự kế thừa nào. Để đảm bảo các bằng lớp cha () và hashCode () được sử dụng, bạn cần phải thế hệ rõ ràng với callSuper
Arne Burmeister

5

đối với các lớp cao cấp có nhiều thành viên, tôi khuyên bạn nên sử dụng @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;
    }
}

Đây là một cách tiếp cận thú vị, giống như nó!
Arne Burmeister,

@Delegate@Target({ElementType.FIELD, ElementType.METHOD}). AInner phải là lĩnh vực trong A.
boriselec

3

Nếu lớp con có nhiều thành viên hơn là lớp cha mẹ, nó có thể được thực hiện không sạch sẽ, nhưng cách ngắn gọn:

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

3

Phiên bản 1.18 của Lombok đã giới thiệu chú thích @SuperBuilder. Chúng ta có thể sử dụng điều này để giải quyết vấn đề của mình theo cách đơn giản hơn.

Bạn có thể tham khảo https://www.baeldung.com/lombok-builder-inheritance#lombok-builder-and-inheritance-3 .

vì vậy trong lớp con của bạn, bạn sẽ cần những chú thích sau:

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

trong lớp cha mẹ của bạn:

@Data
@SuperBuilder
@NoArgsConstructor

0

Là một tùy chọn bạn có thể sử dụng com.fasterxml.jackson.databind.ObjectMapperđể khởi tạo một lớp con từ cha mẹ

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

Bạn vẫn có thể sử dụng bất kỳ lombokchú thích nào trên A và B nếu cần.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.