cara memanggil konstruktor super di Lombok


118

saya ada kelas

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

Saya memiliki kelas B lain

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

lombok melempar kesalahan yang mengatakan tidak dapat menemukan konstruktor A (), secara eksplisit menyebutnya apa yang saya ingin lombok lakukan adalah memberikan anotasi ke kelas b sehingga menghasilkan kode berikut:

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

Apakah kami memiliki anotasi untuk melakukan itu di Lombok?

Jawaban:


169

Ini tidak mungkin dilakukan di Lombok. Meskipun ini akan menjadi fitur yang sangat bagus, itu membutuhkan resolusi untuk menemukan konstruktor kelas super. Kelas super hanya dikenal dengan namanya saat Lombok dipanggil. Menggunakan pernyataan import dan classpath untuk menemukan kelas sebenarnya tidaklah sepele. Dan selama kompilasi Anda tidak bisa hanya menggunakan refleksi untuk mendapatkan daftar konstruktor.

Ini bukan sepenuhnya tidak mungkin tetapi hasil menggunakan resolusi dalam valdan @ExtensionMethodtelah mengajari kami bahwa itu sulit dan rawan kesalahan.

Pengungkapan: Saya adalah pengembang Lombok.


@ roel-spilker Kami memahami kerumitan di baliknya. Tetapi dapatkah Lombok menyediakan inConstructormetode untuk penjelasan konstruktor di mana kita dapat menentukan konstruktor mana yang superharus dimasukkan Lombok ke dalam konstruktor yang dihasilkan?
Manu Manjunath

1
afterConstructor akan bagus juga untuk melakukan beberapa inisialisasi otomatis
Pawel

@ Manu / @ Pawel: lihat permintaan peningkatan lombok: github.com/peichhorn/lombok-pg/issues/78 (saat ini terbuka)
JJ Zabkar

Karena @Builder dalam rilis resmi lihat: github.com/rzwitserloot/lombok/issues/853
Sebastian

4
Masih tidak mungkin?
FearX

22

Lombok Issue # 78 mereferensikan halaman ini https://www.donneo.de/2015/09/16/lomboks-builder-annotation-and-inheritance/ dengan penjelasan yang indah ini:

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

Hasilnya, Anda kemudian dapat menggunakan pembuat yang dihasilkan seperti ini:

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

The dokumentasi resmi menjelaskan ini, tetapi tidak secara eksplisit menunjukkan bahwa Anda dapat memfasilitasi dengan cara ini.

Saya juga menemukan ini berfungsi dengan baik dengan Spring Data JPA.


Dapatkah Anda memberikan contoh penggunaan ini dengan Spring Data JPA?
Marc Zampetti

29
Ini sama sekali tidak menjawab pertanyaan itu. Sebaliknya, ia melakukan pekerjaan dengan tangan, sedangkan pertanyaannya adalah bagaimana cara membuatnya. Pada saat yang sama, itu membuat semuanya lebih membingungkan dengan menyeret @Builder, yang tidak ada hubungannya dengan pertanyaan tersebut.
Jasper

8
Sebenarnya ini sangat berguna bagi mereka yang hanya ingin membuat struktur inheritance kemudian menggunakan builder. Ini adalah 99% alasan saya menggunakan #lombok. Kadang-kadang kita hanya perlu membuat barang-barang untuk membuatnya bekerja seperti yang kita inginkan. Terima kasih @ jj-zabkar
Babajide Prince

tapi kemudian; cukup kode konstruktor arg self + parent. Tidak perlu menggunakan pembangun
Juh_

Ini akan bekerja di STS & eclipse, tetapi ketika Anda membuat file JAR dari aplikasi Anda, kemungkinan besar itu akan gagal. Saya mencoba Both SuperBuilder, Builder untuk inheritence. Keduanya gagal. Hati-hati !!
P Satish Patro

6

Lombok tidak mendukung itu juga ditunjukkan dengan membuat @Valuekelas beranotasi final(seperti yang Anda ketahui dengan menggunakan @NonFinal).

Satu-satunya solusi yang saya temukan adalah mendeklarasikan semua anggota final sendiri dan menggunakan @Dataanotasi sebagai gantinya. Subkelas tersebut perlu dianotasi oleh @EqualsAndHashCodedan memerlukan konstruktor all args yang eksplisit karena Lombok tidak tahu cara membuatnya menggunakan kelas super all args:

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

Terutama konstruktor dari subclass membuat solusi sedikit tidak rapi untuk superclass dengan banyak anggota, maaf.


1
Bisakah Anda menjelaskan sedikit lebih banyak mengapa "subclass perlu diberi anotasi oleh @EqualsAndHashCode"? Bukankah anotasi ini disertakan oleh @Data? Thx :)
Gerard Bosch

1
@GerardB @Datajuga membuat sama dengan () dan hashCode () tetapi tidak peduli tentang warisan apa pun. Untuk memastikan superclass sama dengan () dan hashCode () digunakan, Anda memerlukan generasi eksplisit dengan callSuper
Arne Burmeister

5

untuk superclass dengan banyak anggota, saya sarankan Anda menggunakan @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;
    }
}

Ini adalah pendekatan yang menarik, suka!
Arne Burmeister

@Delegateadalah @Target({ElementType.FIELD, ElementType.METHOD}). AInner harus di lapangan A.
boriselec

3

Jika kelas anak memiliki lebih banyak anggota, daripada orang tua, itu bisa dilakukan tidak terlalu bersih, tapi cara singkat:

@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

Sebagai opsi yang dapat Anda gunakan com.fasterxml.jackson.databind.ObjectMapperuntuk menginisialisasi kelas anak dari orang tua

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

Anda tetap dapat menggunakan lombokanotasi apa pun pada A dan B jika perlu.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.