Lombok: Pembangun harus mendukung bidang dari kelas induk

Dibuat pada 15 Jul 2015  ·  61Komentar  ·  Sumber: projectlombok/lombok

_Bermigrasi dari Google Code (edisi 818)_

Komentar yang paling membantu

Saya membuat permintaan tarik (#1337, hehe) yang membahas masalah ini.

Saya mengikuti ide @Krzychek yang disebutkan sebelumnya: Hasilkan konstruktor pada tipe yang menggunakan pembangun sebagai parameter dan menggunakan bidang dari pembangun untuk mengatur bidang instance baru. Metode Builder.build() memanggil konstruktor ini.

Fitur ini hanya berfungsi untuk pembuat tipe dan harus diaktifkan secara eksplisit, agar tidak merusak implementasi yang ada, yang mungkin bergantung pada konstruktor all-args yang dihasilkan oleh @Builder .

Contoh:

@Builder(extendable = true)
public class Parent {
    <strong i="12">@Getter</strong>
    private String parentString;
}

@Builder(inherit = true)
public class Child extends Parent {
    <strong i="13">@Getter</strong>
    private String childString;
}

public class TestBuilder {
    public static void main(String[] args) {
        Child child = (Child) Child.builder()
                .childString("childStringValue")
                .parentString("parentStringValue")
                .build();
        assert("childStringValue".equals(child.getChildString()));
        assert("parentStringValue".equals(child.getParentString()));
    }
}

Semua 61 komentar

:bust_in_silhouette: elreydetodo :jam8: 29 Mei 2015 pukul 21:54 UTC

Dalam proyek yang sedang saya kerjakan, saya memiliki kelas dasar abstrak dengan tiga sub-kelas. Katakanlah kelas dasar memiliki bidang "id", "name", dan "type". Saya berharap salah satu subkelas yang dianotasi dengan @ Builder dapat mengatur bidang-bidang itu dalam implementasi pembangunnya.

Pada catatan terkait (meskipun mungkin memerlukan bug terpisah), @ AllArgsConstructor mungkin harus mempertimbangkan bidang dari kelas induk.

Saya pikir masuk akal jika ini menjadi opsi yang harus saya berikan ke anotasi (yaitu @ Builder(includeFromParent=true)).

:bust_in_silhouette: Maaartinus :jam8: 30 Mei 2015 pukul 07:15 UTC

Untuk Builder, lihat
https://groups.google.com/d/msg/project-lombok/1S7Y7sHXZS8/Rdzj_U4Tvi0J

Untuk alasan mengapa itu belum diterapkan, lihat
https://groups.google.com/d/msg/project-lombok/-6b9dPH8qAw/0rzZW6ZAgJIJ

Masalah terkait
masalah #740

_Akhir migrasi_

Akan lebih baik untuk memasukkan solusi dalam dokumentasi resmi (di @Builder)
mis: https://www.donneo.de/2015/09/16/lomboks-builder-annotation-and-inheritance/

<strong i="8">@AllArgsConstructor</strong>
public class Parent {
  private String a;
}

public class Child extends Parent {
  private String b;

  <strong i="9">@Builder</strong>
  public Child(String a, String b){
    super(a);
    this.b = b;
  }
}

public static void main(String[] ...){
  Child.builder().a("testA").b("testB").build();
}

Pola pembangun yang mendukung pewarisan tanpa masalah ayam dan telur - bukan kode terbersih, berantakan dengan obat generik, tetapi karena ini akan dihasilkan, mungkin patut dipertimbangkan?

public class Parent {

    private final int a;

    protected Parent(final AbstractParentBuilder builder) {
        this.a = builder.a;
    }

    public static final class Builder extends AbstractParentBuilder<Parent, Builder> {
        public Parent build() {
            return new Parent(this);
        }
    }

    protected static abstract class AbstractParentBuilder<T extends Parent, B extends AbstractParentBuilder<T, ?>> {

        private int a;

        public B withA(int a) {
            this.a = a;
            return (B) this;
        }
    }
}


public class Child extends Parent {

    private final int b;

    protected Child(final AbstractChildBuilder builder) {
        super(builder);
        this.b = builder.b;
    }

    public static final class Builder extends AbstractChildBuilder<Child, Builder> {
        public Child build() {
            return new Child(this);
        }
    }

    protected static abstract class AbstractChildBuilder<T extends Child, B extends AbstractChildBuilder<T, ?>> extends Parent.AbstractParentBuilder<T, B> {

        private int b;

        public B withB(int b) {
            this.b = b;
            return (B) this;
        }
    }
}

Dengan itu, kode di bawah ini valid:

Child child = new Child.Builder().withB(1).withA(2).build();

Kami dapat menghilangkan gips, menambahkan beberapa metode abstrak self(), diimplementasikan dalam Pembangun beton.

Saya datang ke sini dari peichhorn/lombok-pg#78, yang memiliki banyak +1. Saya harap pengelola di sini menyadari hal ini.

+1

+1

+1

+1

+1

+1

+1

+1

+1

+1

includeFromParent=true tidak sepele karena membutuhkan resolusi. Mulai sekarang, silakan gunakan tombol jempol ke atas di bawah komentar pertama untuk membagikan suara Anda.

@rspilker ada banyak upvotes lain untuk ini, tetapi orang-orang membuatnya di repo yang salah: https://github.com/peichhorn/lombok-pg/issues/78#issuecomment -131239419

+1

+1

+1

+1

+1

+1

+1

+1

+1.

Solusi

@drivept Terima kasih atas solusinya. Tetapi Anda harus membuat konstruktor untuk semua bidang, yang bisa sangat besar untuk beberapa kelas entitas! Sayang sekali @AllArgConstructor juga tidak mendukung pewarisan. :menjulurkan lidah menutup mata:

+1

+1

+1

+1

+1

+1

+1

+1

+1

Masalah ini tidak dapat diperbaiki tanpa perubahan besar pada Lombok, yaitu, menunda pemrosesan Lombok setelah analisis sematik kompiler. Jadi, kami telah menyelidiki solusi untuk masalah ini. Ini adalah solusi yang kami buat.

Mari kita asumsikan kita memiliki dua kelas: superclass Parent , dan subclass Child .
Tidak mungkin mengakses informasi tipe APAPUN dari superclass saat membuat Builder untuk subclass. Oleh karena itu, kami juga membuat builder untuk superclass.
Idenya adalah kita menggunakan pembangun superclass ini untuk menyuntikkan nilainya ke dalam instance subclass.

Kelas super:

<strong i="12">@NoArgsConstructor</strong>
@Builder(builderMethodName="parentBuilder", applyOn=true) 
public abstract class Parent {
    <strong i="13">@Getter</strong> <strong i="14">@Setter</strong>
    private String parentString;

    <strong i="15">@Getter</strong> <strong i="16">@Setter</strong> <strong i="17">@Singular</strong>
    private List<String> parentValues;
}

Subkelas:

<strong i="21">@Builder</strong>
public class Child extends Parent {
    <strong i="22">@Getter</strong> <strong i="23">@Setter</strong>
    private String childString;

    <strong i="24">@Getter</strong> <strong i="25">@Setter</strong> <strong i="26">@Singular</strong>
    private List<String> childValues;
}

Cara Penggunaan:

Child child = Child.builder()
        .childString("childValue")
        .childValue("childValue1")
        .childValue("childValue2")
        .build();
Parent.parentBuilder()
        .parentString("parentValue")
        .parentValue("parentValue1")
        .parentValue("parentValue2")
        .applyOn(child);

Catatan:

  • Konsekuensi langsung dari memiliki builder untuk setiap kelas dalam hierarki pewarisan adalah bahwa hanya satu builder yang dapat digunakan sebagai builder nyata yang membuat instance menggunakan konstruktor. Semua builder lain hanya perlu "diterapkan pada" instance yang ada.
  • Metode applyOn() bergantung pada penetapan nilai ke bidang instance secara langsung. Jika @Builder ditempatkan pada konstruktor yang parameternya diberi nama berbeda dari bidang kelas, metode applyOn() tidak dapat dibuat.
  • Builder harus memiliki builderMethodNames di setiap kelas dalam hierarki.
  • Kami mengubah bahwa @Builder juga dikompilasi pada kelas abstrak, cukup dengan menghilangkan metode build() .
  • Nama metode applyOn() dapat dikustomisasi melalui parameter anotasi.

Kami menerapkan ini untuk pembuat Eclipse, dan itu bekerja dengan cukup baik. :-)
@rspilker , @rzwitserloot : Apakah ini sesuatu yang akan Anda pertimbangkan untuk disertakan? Jika ini masalahnya, kami akan terus mengerjakannya, terutama dengan menyediakan implementasi javac.

Anda dapat membalikkan pola Builder dan mengambil instance builder sebagai parameter konstruktor, dengan cara ini Builder dapat dengan mudah diwarisi, dengan menambahkan param di kelas anak. Jadi Anda bisa menambahkan @InheriteBuilder , beberapa bidang anotasi, atau sesuatu seperti itu untuk menangani 'resolusi' pewarisan secara eksplisit oleh pengguna bukan kompiler.

class A {
    final int x;
    A(Builder b) {
        this.x = b.x;
    }

    static class Builder {
        public int x;
        A build() {
            return new A(this);
        }
    }
}

class B extends A {
    final int y;
    B(Builder b) {
        super(b);
        this.y = b.y;
    }

    static class Builder extends A.Builder {
        public int y;

        <strong i="7">@Override</strong> B build() {
            return new B(this);
        }
    }
}

_Saya menghapus setter pada builder untuk singkatnya;_

+1

+1

+1

Bisakah kalian berhenti memposting +1 yang tidak berguna ini di bagian bawah, membuang waktu Anda dan semua orang yang telah berlangganan halaman ini.

Jika Anda ingin masalah ini mendapat perhatian lebih, posting jempol untuk posting pertama, bukan di bawah

Saya membuat permintaan tarik (#1337, hehe) yang membahas masalah ini.

Saya mengikuti ide @Krzychek yang disebutkan sebelumnya: Hasilkan konstruktor pada tipe yang menggunakan pembangun sebagai parameter dan menggunakan bidang dari pembangun untuk mengatur bidang instance baru. Metode Builder.build() memanggil konstruktor ini.

Fitur ini hanya berfungsi untuk pembuat tipe dan harus diaktifkan secara eksplisit, agar tidak merusak implementasi yang ada, yang mungkin bergantung pada konstruktor all-args yang dihasilkan oleh @Builder .

Contoh:

@Builder(extendable = true)
public class Parent {
    <strong i="12">@Getter</strong>
    private String parentString;
}

@Builder(inherit = true)
public class Child extends Parent {
    <strong i="13">@Getter</strong>
    private String childString;
}

public class TestBuilder {
    public static void main(String[] args) {
        Child child = (Child) Child.builder()
                .childString("childStringValue")
                .parentString("parentStringValue")
                .build();
        assert("childStringValue".equals(child.getChildString()));
        assert("parentStringValue".equals(child.getParentString()));
    }
}

Akan sangat bagus untuk mendapatkan umpan balik dari pengembang proyek. Jika PR saya memiliki peluang untuk disertakan, saya akan memperbaiki konflik saat ini di PR agar dapat digabungkan kembali, dan/atau meningkatkan kode jika Anda memiliki komentar.
Selain itu, saya dapat mencoba menerapkan ide generik @msarniak untuk menyingkirkan pemeran.

@rzwitserloot Ada tanggapan untuk pertanyaan di atas?

+1

+1

Solusi yang diberikan sejauh ini tidak terlalu berguna atau bagus. Berikut ini contohnya:
Saya mencoba membuat warisan dengan kontrak dari antarmuka dan implementasi default untuk properti untuk dibagikan dan pada akhirnya memiliki objek yang tidak dapat diubah. 3 hal yang membantu saya mencapainya adalah: pewarisan (untuk menyediakan bidang/konstruktor), pola pembangun (membuat hal-hal tidak dapat diubah), dan lombok (penghapusan penulisan kode boilerplate).
Apa yang disarankan pada dasarnya melanggar setidaknya satu dari tujuan saya (dan lebih mungkin 2). Jika saya akan menulis konstruktor saya sendiri, saya tidak membuat semuanya bersih, menghemat banyak waktu dengan lombok, dan saya juga mengalahkan tujuan pewarisan.

Sepakat. Masih belum ada solusi yang ideal.

Harap berhenti mengirimkan +1 ke permintaan - itu hanya menghasilkan komentar yang tidak perlu dan mencemari pos. Jika Anda setuju/tidak setuju silakan gunakan komentar/reaksi/emoji yang tepat di atas.

Masih tidak ada kemajuan dalam masalah ini?

+1

+2 ;)

+1

Saya melihat banyak +1, tetapi apakah masalah ini sudah diperbaiki?

@sumeettakalkar Masalah masih terbuka. #1337 memiliki diskusi & solusi yang bagus.

Saya tidak melihat solusi bersih. Mungkin saya perlu menggunakan lombok tanpa pola pembangun karena proyek saya menggunakan banyak warisan.

Sebelumnya saya pikir itu akan mudah atau mungkin saya bisa membuat beberapa peretasan di sekitar @Builder. Bagaimanapun juga, layak untuk ditonton #1337

@janrieke Terima kasih untuk PR.

+1 tolong dukung ini

Ada PR baru yang datang dengan pendekatan yang dimodifikasi, karena pendekatan pertama saya masih membutuhkan casting, yang tidak terlihat bagus.
Memposting "+1" itu tidak akan mempercepat. Harap bersabar sebentar lagi.

Permintaan tarik baru #1711 dengan pendekatan yang ditingkatkan.

Sepertinya #1711 dirilis sebagai eksperimental di versi 1.18.2, jadi masalah ini dapat ditutup?

Apakah halaman ini membantu?
0 / 5 - 0 peringkat