Kelas yang tidak bisa diubah?

Jawaban:

136

Apa itu objek yang tidak bisa diubah?

Objek yang tidak dapat diubah adalah objek yang tidak akan berubah status setelah dibuat instance-nya.

Bagaimana cara membuat objek tidak berubah?

Secara umum, objek yang tidak dapat diubah dapat dibuat dengan mendefinisikan kelas yang anggotanya tidak ada yang terpapar, dan tidak memiliki penyetel.

Kelas berikut akan membuat objek yang tidak dapat diubah:

class ImmutableInt {
  private final int value;

  public ImmutableInt(int i) {
    value = i;
  }

  public int getValue() {
    return value;
  }
}

Seperti yang bisa dilihat pada contoh di atas, nilai dari ImmutableInthanya bisa disetel ketika objek dibuat, dan dengan hanya memiliki getter ( getValue), status objek tidak bisa diubah setelah pembuatan instance.

Namun, harus berhati-hati bahwa semua objek yang direferensikan oleh objek juga harus tetap, atau status objek dapat diubah.

Misalnya, mengizinkan referensi ke larik atau ArrayListdiperoleh melalui getter akan memungkinkan keadaan internal berubah dengan mengubah larik atau kumpulan:

class NotQuiteImmutableList<T> {
  private final List<T> list;

  public NotQuiteImmutableList(List<T> list) {
    // creates a new ArrayList and keeps a reference to it.
    this.list = new ArrayList(list); 
  }

  public List<T> getList() {
    return list;
  }
}

Masalah dengan kode di atas adalah, bahwa ArrayListdapat diperoleh melalui getListdan dimanipulasi, yang mengarah ke keadaan objek itu sendiri yang akan diubah, oleh karena itu, tidak dapat diubah.

// notQuiteImmutableList contains "a", "b", "c"
List<String> notQuiteImmutableList= new NotQuiteImmutableList(Arrays.asList("a", "b", "c"));

// now the list contains "a", "b", "c", "d" -- this list is mutable.
notQuiteImmutableList.getList().add("d");

Salah satu cara untuk mengatasi masalah ini adalah dengan mengembalikan salinan larik atau koleksi saat dipanggil dari getter:

public List<T> getList() {
  // return a copy of the list so the internal state cannot be altered
  return new ArrayList(list);
}

Apa keuntungan dari kekekalan?

Keuntungan dari kekekalan hadir dengan konkurensi. Sulit untuk mempertahankan kebenaran pada objek yang bisa berubah, karena beberapa utas mungkin mencoba mengubah status objek yang sama, yang menyebabkan beberapa utas melihat status berbeda dari objek yang sama, tergantung pada waktu pembacaan dan penulisan ke objek yang sama. obyek.

Dengan memiliki objek yang tidak dapat diubah, seseorang dapat memastikan bahwa semua utas yang melihat objek akan melihat status yang sama, karena status objek yang tidak dapat diubah tidak akan berubah.

burung coobird
sumber
5
Keamanan benang (penting untuk konkurensi) bukan satu-satunya keuntungan dari kekekalan; ini juga berarti Anda tidak perlu membuat salinan defensif dari objek, dan mencegah bug karena Anda tidak dapat secara tidak sengaja memodifikasi objek yang tidak seharusnya dimodifikasi.
Jesper
7
Daripada mengembalikan salinan daftar, Anda juga return Collections.unmodifiableList(list);dapat mengembalikan tampilan hanya-baca di daftar.
Jesper
19
Kelas juga harus dibuat final. Jika tidak, itu dapat diperpanjang dengan metode penyetel (atau jenis metode mutasi lain dan bidang yang bisa berubah).
Abhinav Sarkar
6
@AbhinavSarkar Tidak harus finaljika kelas hanya memiliki privatebidang karena tidak dapat diakses dari subkelas.
icza
3
Kelas @icza harus final, karena kita memiliki metode pengambil publik, kita dapat memperluas kelas dan mengganti metode pengambil dan kemudian mengubah bidang dengan cara kita..jadi tidak dapat diubah lagi
sunil
19

Selain jawaban yang sudah diberikan, saya merekomendasikan Anda membaca tentang immutability in Effective Java, 2nd Ed., Karena ada beberapa detail yang mudah terlewat (mis. Salinan defensif). Plus, Effective Java 2nd Ed. harus dibaca oleh setiap pengembang Java.

Fabian Steeg
sumber
3
Ini adalah sumber daya yang tepat untuk dilihat. Manfaat yang disebutkan berkisar mulai dari kode yang kurang rentan terhadap kesalahan hingga keamanan utas.
gpampara
6

Anda membuat kelas tidak berubah seperti ini:

public final class Immutable
{
    private final String name;

    public Immutable(String name) 
    {
        this.name = name;
    }

    public String getName() { return this.name; } 

    // No setter;
}

Berikut adalah persyaratan untuk membuat kelas Java tidak dapat diubah:

  • Kelas harus dideklarasikan sebagai final(Sehingga kelas anak tidak dapat dibuat)
  • Anggota di kelas harus dideklarasikan sebagai final(Sehingga kita tidak dapat mengubah nilainya setelah pembuatan objek)
  • Tulis metode Getter untuk semua variabel di dalamnya untuk mendapatkan nilai Anggota
  • Tidak ada metode Setter

Kelas yang tidak dapat diubah berguna karena
- Kelas tersebut aman untuk thread.
- Mereka juga mengekspresikan sesuatu yang dalam tentang desain Anda: "Tidak dapat mengubah ini.", Jika diterapkan, itulah yang Anda butuhkan.

duffymo
sumber
5

Kekekalan dapat dicapai terutama dengan dua cara:

  • menggunakan finalatribut instance untuk menghindari penugasan ulang
  • menggunakan antarmuka kelas yang tidak mengizinkan operasi apa pun yang dapat mengubah apa yang ada di dalam kelas Anda (hanya getter dan tidak ada setter

Keuntungan dari kekekalan adalah asumsi yang dapat Anda buat pada objek ini:

  • Anda mendapatkan aturan tanpa efek samping (yang sangat populer pada bahasa pemrograman fungsional) dan memungkinkan Anda untuk menggunakan objek dalam lingkungan yang bersamaan dengan lebih mudah, karena Anda tahu bahwa objek tidak dapat diubah dengan cara atom atau non atom saat mereka digunakan oleh banyak utas
  • implementasi bahasa dapat memperlakukan objek-objek ini dengan cara yang berbeda, menempatkannya di zona memori yang digunakan untuk data statis, memungkinkan penggunaan objek-objek ini dengan lebih cepat dan lebih aman (inilah yang terjadi di dalam JVM untuk string)
Mendongkrak
sumber
Kekal tidak sama dengan efek samping yang sama gratis. Misalnya, objek yang tidak dapat diubah dapat menghasilkan efek samping seperti masuk ke file. Agak tidak akurat untuk mengatakan membuat suatu objek tidak berubah juga membuatnya bebas efek samping.
Grundlefleck
1
@Grundleflek, saya pikir ini mungkin membelah rambut. Kelas tidak dapat diubah jika memodifikasi file log sebagai bagian dari kontraknya dan file log tersebut dapat diakses oleh kelas lain. Jika file log disembunyikan dari kelas lain, dan bukan bagian dari kontrak kelas maka kelas tersebut secara efektif tidak dapat diubah dan benar-benar untuk semua maksud dan tujuan bebas efek samping. Pengenalan (tanpa sumber) di halaman efek samping Wikipedia berbunyi "... sebuah ekspresi dikatakan memiliki efek samping jika, selain menghasilkan nilai, ekspresi juga memodifikasi beberapa status atau memiliki interaksi yang dapat diamati dengan fungsi pemanggil."
Jeff Axelrod
3

Kelas yang tidak dapat diubah tidak dapat menetapkan kembali nilai setelah dibuat instance-nya. Konstruktor menetapkan nilai ke variabel pribadinya. Sampai objek menjadi null, nilai tidak dapat diubah karena tidak tersedianya metode penyetel.

menjadi kekal harus memuaskan berikut,

  • Semua variabel harus privat .
  • Tidak ada metode mutator (penyetel) yang disediakan.
  • Hindari metode overriding dengan membuat final kelas (Kekekalan Kuat) atau metode akhir (Kekekalan minggu).
  • Gandakan secara mendalam jika berisi kelas non primitif atau dapat berubah.

/**
* Strong immutability - by making class final
*/
public final class TestImmutablity {

// make the variables private
private String Name;

//assign value when the object created
public TestImmutablity(String name) {
this.Name = name;
}

//provide getters to access values
public String getName() {

return this.Name;
}
}

Keuntungan: Objek yang tidak dapat diubah berisi nilai yang diinisialisasi sampai mati.

java-immutable-class-short-note

Kandy
sumber
2

Kelas yang tidak dapat diubah adalah kelas yang objeknya tidak dapat diubah setelah dibuat.

Kelas yang tidak dapat diubah berguna untuk

  • Tujuan cache
  • Lingkungan bersamaan (ThreadSafe)
  • Sulit untuk warisan
  • Nilai tidak dapat diubah di lingkungan mana pun

Contoh

Kelas String

Contoh Kode

public final class Student {
    private final String name;
    private final String rollNumber;

    public Student(String name, String rollNumber) {
        this.name = name;
        this.rollNumber = rollNumber;
    }

    public String getName() {
        return this.name;
    }

    public String getRollNumber() {
        return this.rollNumber;
    }
}
Yasir Shabbir Choudhary
sumber
2

Bagaimana cara membuat kelas Java tidak dapat diubah?

Dari JDK 14+ yang memiliki JEP 359 , kita bisa menggunakan " records". Ini adalah cara paling sederhana dan bebas repot untuk membuat kelas Immutable.

Kelas rekaman adalah pembawa transparan yang dangkal tidak berubah untuk sekumpulan bidang tetap yang dikenal sebagai rekaman componentsyang memberikan statedeskripsi untuk rekaman tersebut. Masing-masing componentmemunculkan finalbidang yang menyimpan nilai yang diberikan dan accessormetode untuk mengambil nilai. Nama bidang dan nama pengakses cocok dengan nama komponen.

Mari pertimbangkan contoh membuat persegi panjang yang tidak bisa diubah

record Rectangle(double length, double width) {}

Tidak perlu mendeklarasikan konstruktor apa pun, tidak perlu mengimplementasikan metode sama & hashCode. Setiap Catatan membutuhkan nama dan deskripsi negara bagian.

var rectangle = new Rectangle(7.1, 8.9);
System.out.print(rectangle.length()); // prints 7.1

Jika Anda ingin memvalidasi nilai selama pembuatan objek, kita harus secara eksplisit mendeklarasikan konstruktor.

public Rectangle {

    if (length <= 0.0) {
      throw new IllegalArgumentException();
    }
  }

Badan record dapat mendeklarasikan metode statis, kolom statis, penginisialisasi statis, konstruktor, metode instance, dan jenis bertingkat.

Metode Instance

record Rectangle(double length, double width) {

  public double area() {
    return this.length * this.width;
  }
}

bidang statis, metode

Karena status harus menjadi bagian dari komponen, kami tidak dapat menambahkan bidang contoh ke rekaman. Tapi, kita bisa menambahkan field dan metode statis:

record Rectangle(double length, double width) {

  static double aStaticField;

  static void aStaticMethod() {
    System.out.println("Hello Static");
  }
}

apa perlunya kekekalan dan apakah ada keuntungan menggunakan ini?

Jawaban yang diposting sebelumnya cukup baik untuk membenarkan kebutuhan akan kekekalan dan itu pro

Bhanu Hoysala
sumber
0

Cara lain untuk membuat objek yang tidak dapat diubah menggunakan perpustakaan Immutables.org :

Dengan asumsi bahwa dependensi yang diperlukan telah ditambahkan, buat kelas abstrak dengan metode pengakses abstrak. Anda dapat melakukan hal yang sama dengan membuat anotasi dengan antarmuka atau bahkan anotasi (@interface):

package info.sample;

import java.util.List;
import java.util.Set;
import org.immutables.value.Value;

@Value.Immutable
public abstract class FoobarValue {
  public abstract int foo();
  public abstract String bar();
  public abstract List<Integer> buz();
  public abstract Set<Long> crux();
}

Sekarang dimungkinkan untuk membuat dan kemudian menggunakan implementasi immutable yang dihasilkan:

package info.sample;

import java.util.List;

public class FoobarValueMain {
  public static void main(String... args) {
    FoobarValue value = ImmutableFoobarValue.builder()
        .foo(2)
        .bar("Bar")
        .addBuz(1, 3, 4)
        .build(); // FoobarValue{foo=2, bar=Bar, buz=[1, 3, 4], crux={}}

    int foo = value.foo(); // 2

    List<Integer> buz = value.buz(); // ImmutableList.of(1, 3, 4)
  }
}
Justinas Jakavonis
sumber
0

Kelas yang tidak dapat diubah hanyalah kelas yang instansinya tidak dapat diubah.

Semua informasi yang terkandung dalam setiap contoh ditetapkan selama masa pakai objek, sehingga tidak ada perubahan yang dapat diamati.

Kelas yang tidak berubah lebih mudah untuk dirancang, diimplementasikan, dan digunakan daripada kelas yang bisa berubah.

Untuk membuat kelas tetap, ikuti lima aturan berikut:

  1. Jangan berikan metode yang mengubah status objek

  2. Pastikan kelas tidak dapat diperpanjang.

  3. Buat semua bidang menjadi final.

  4. Jadikan semua bidang pribadi.

  5. Pastikan akses eksklusif ke semua komponen yang bisa berubah.

Objek yang tidak bisa diubah secara inheren aman untuk benang; mereka tidak membutuhkan sinkronisasi.

Objek yang tidak dapat diubah dapat dibagikan dengan bebas.

Objek yang tidak bisa diubah menjadi blok bangunan yang bagus untuk objek lain

sandeep vanama
sumber
0

@Jack, Memiliki bidang akhir dan penyetel di kelas tidak akan membuat kelas tidak dapat diubah. kata kunci terakhir hanya memastikan bahwa variabel tidak pernah ditetapkan ulang. Anda perlu mengembalikan salinan lengkap dari semua bidang dalam metode pengambil. Ini akan memastikan bahwa, setelah mendapatkan objek dari metode getter, keadaan internal objek tidak terganggu.

Amit Toor
sumber
-1

Sebagai penutur bahasa Inggris bukan penutur asli, saya tidak menyukai interpretasi umum dari "kelas yang tidak dapat diubah" menjadi "objek kelas yang dibangun tidak dapat diubah"; sebaliknya, saya sendiri cenderung menafsirkan bahwa sebagai "objek kelas itu sendiri tidak dapat diubah".

Yang mengatakan, "kelas yang tidak berubah" adalah sejenis objek yang tidak berubah. Bedanya saat menjawab apa manfaatnya. Untuk pengetahuan / interpretasi saya, kelas tetap mencegah objeknya dari modifikasi perilaku runtime.

bbgw
sumber
-1

Sebagian besar jawaban di sini bagus, dan beberapa menyebutkan aturannya tetapi saya merasa senang untuk menuliskan mengapa & kapan kita perlu mengikuti aturan ini. Demikian penjelasan di bawah ini

  • Deklarasikan variabel anggota sebagai 'final' - Saat kita mendeklarasikannya sebagai final, compiler memaksa kita untuk menginisialisasinya. Kita dapat menginisialisasi secara langsung, dengan konstruktor default, dengan konstruktor arg. (Lihat kode contoh di bawah) dan setelah inisialisasi kita tidak dapat memodifikasinya karena sudah final.
  • Dan tentu saja jika kita mencoba menggunakan Setter untuk variabel terakhir tersebut, compiler akan menampilkan error.

    public class ImmutableClassExplored {
    
        public final int a; 
        public final int b;
    
        /* OR  
        Generally we declare all properties as private, but declaring them as public 
        will not cause any issues in our scenario if we make them final     
        public final int a = 109;
        public final int b = 189;
    
         */
        ImmutableClassExplored(){
            this. a = 111;
            this.b = 222;
        }
    
        ImmutableClassExplored(int a, int b){
            this.a = a;
            this.b= b;
        }
    }
    

Apakah kita perlu mendeklarasikan kelas sebagai 'final'?

  • Tanpa kata kunci terakhir dalam deklarasi kelas, kelas dapat diturunkan. Jadi subclass bisa mengganti metode pengambil. Di sini kita harus mempertimbangkan dua skenario:

1. Hanya memiliki anggota primitif: Kami tidak memiliki masalah Jika kelas hanya memiliki anggota primitif, maka kami tidak perlu mendeklarasikan kelas sebagai final.

2.Memiliki Objek sebagai variabel anggota: Jika kita memiliki objek sebagai variabel anggota maka kita harus membuat anggota dari objek tersebut juga final. Berarti kita perlu melintasi jauh di bawah pohon dan menjadikan semua objek / primitif sebagai final yang mungkin tidak bisa dilakukan setiap saat. Jadi solusinya adalah membuat kelas final yang mencegah pewarisan. Jadi tidak ada pertanyaan tentang subclass overriding getter method.

Srikant M
sumber
-1

@Value annotation of Lombok bisa digunakan untuk menghasilkan kelas yang tidak bisa diubah. Ini sesederhana kode di bawah ini.

@Value
public class LombokImmutable {
    int id;
    String name;
}

Sesuai dokumentasi di situs Lombok:

@Value adalah varian tetap dari @Data; semua bidang dijadikan pribadi dan final secara default, dan penyetel tidak dibuat. Kelas itu sendiri juga dibuat final secara default, karena kekekalan bukanlah sesuatu yang dapat dipaksakan ke dalam subkelas. Seperti @Data, metode toString (), equals (), dan hashCode () yang berguna juga dihasilkan, setiap bidang mendapatkan metode pengambil, dan konstruktor yang mencakup setiap argumen (kecuali bidang akhir yang diinisialisasi dalam deklarasi bidang) juga dihasilkan .

Contoh yang berfungsi sepenuhnya dapat ditemukan di sini.

Anubhav
sumber
Sebelum menjawab pertanyaan lama yang memiliki jawaban yang diterima (cari yang hijau ✓) serta jawaban lain, pastikan jawaban Anda menambahkan sesuatu yang baru atau berguna terkait dengan itu. Harap berhati-hati saat menjawab pertanyaan OP Bagaimana seseorang dapat membuat kelas Java tidak dapat diubah, apa kebutuhan dari ketidakmampuan dan apakah ada keuntungan menggunakan ini? . - Anda hanya memberikan sebagian jawaban yang hanya menyatakan bahwa seseorang dapat menggunakan Lombok, framework / library pihak ketiga, yang belum tentu tentang pertanyaan OP. Juga Java 15 sudah keluar, mengapa menggunakan Lombok saat Java recordbisa digunakan?
Ivo Mori
Saya memberikan salah satu opsi tentang bagaimana mencapai ini. Tidak semua orang menggunakan Java 15, sebagian besar aplikasi saat ini berjalan pada versi sebelumnya, jadi Anda mengatakan mengapa menggunakan Lombok tidak masuk akal. Dalam proyek saya saat ini, kami baru-baru ini bermigrasi ke Java 11 dan menggunakan Lombok untuk mencapai kelas yang tidak dapat diubah. Juga, jawaban saya adalah tambahan dari jawaban apa yang sudah ada. Apa yang tidak dapat diubah telah terjawab, saya kira Anda mengharapkan saya untuk menulis ulang itu hanya untuk penyelesaian.
Anubhav
Cukup adil. Saya tidak memilih bawah atau pun atas. Saya mencoba menunjukkan alasan mengapa dua orang lainnya menolak jawaban ini. Terserah Anda apakah dan bagaimana Anda ingin mengedit jawaban Anda.
Ivo Mori