Bagaimana seseorang dapat membuat kelas Java tidak dapat diubah, apa kebutuhan kekekalan dan apakah ada keuntungan menggunakan ini?
Bagaimana seseorang dapat membuat kelas Java tidak dapat diubah, apa kebutuhan kekekalan dan apakah ada keuntungan menggunakan ini?
Jawaban:
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 ImmutableInt
hanya 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 ArrayList
diperoleh 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 ArrayList
dapat diperoleh melalui getList
dan 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.
return Collections.unmodifiableList(list);
dapat mengembalikan tampilan hanya-baca di daftar.
final
. Jika tidak, itu dapat diperpanjang dengan metode penyetel (atau jenis metode mutasi lain dan bidang yang bisa berubah).
final
jika kelas hanya memiliki private
bidang karena tidak dapat diakses dari subkelas.
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.
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:
final
(Sehingga kelas anak tidak dapat dibuat)final
(Sehingga kita tidak dapat mengubah nilainya setelah pembuatan objek)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.
Kekekalan dapat dicapai terutama dengan dua cara:
final
atribut instance untuk menghindari penugasan ulangKeuntungan dari kekekalan adalah asumsi yang dapat Anda buat pada objek ini:
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,
/**
* 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.
Kelas yang tidak dapat diubah adalah kelas yang objeknya tidak dapat diubah setelah dibuat.
Kelas yang tidak dapat diubah berguna untuk
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;
}
}
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 components
yang memberikan state
deskripsi untuk rekaman tersebut. Masing-masing component
memunculkan final
bidang yang menyimpan nilai yang diberikan dan accessor
metode 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
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)
}
}
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:
Jangan berikan metode yang mengubah status objek
Pastikan kelas tidak dapat diperpanjang.
Buat semua bidang menjadi final.
Jadikan semua bidang pribadi.
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
@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.
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.
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
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'?
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.
@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.
record
bisa digunakan?