Bir Java sınıfı nasıl değişmez hale getirilebilir, değişmezlik ihtiyacı nedir ve bunu kullanmanın herhangi bir avantajı var mı?
@Immutable
ek açıklamayı jcabi-yönlerinden
Bir Java sınıfı nasıl değişmez hale getirilebilir, değişmezlik ihtiyacı nedir ve bunu kullanmanın herhangi bir avantajı var mı?
@Immutable
ek açıklamayı jcabi-yönlerinden
Yanıtlar:
Değişmez nesne nedir?
Değişmez bir nesne, somutlaştırıldıktan sonra durumunu değiştirmeyecek bir nesnedir.
Bir nesne nasıl değişmez hale getirilir?
Genel olarak, değişmez bir nesne, hiçbir üyesine maruz kalmayan ve ayarlayıcıları olmayan bir sınıf tanımlanarak yapılabilir.
Aşağıdaki sınıf, değişmez bir nesne oluşturacaktır:
class ImmutableInt {
private final int value;
public ImmutableInt(int i) {
value = i;
}
public int getValue() {
return value;
}
}
Yukarıdaki örnekte görülebileceği gibi, değeri ImmutableInt
yalnızca nesne somutlaştırıldığında ayarlanabilir ve yalnızca bir alıcıya ( getValue
) sahip olarak nesnenin durumu somutlaştırmadan sonra değiştirilemez.
Bununla birlikte, nesne tarafından atıfta bulunulan tüm nesnelerin de değişmez olması gerektiğine veya nesnenin durumunu değiştirmenin mümkün olabileceğine dikkat edilmelidir.
Örneğin, bir diziye bir başvuruya veya ArrayList
bir alıcı aracılığıyla elde edilmesine izin vermek, diziyi veya koleksiyonu değiştirerek dahili durumun değişmesine izin verir:
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;
}
}
Yukarıdaki kodla ilgili sorun , nesnenin kendisinin durumunun değiştirilmesine, dolayısıyla değişmez olmamasına yol açacak ArrayList
şekilde elde edilebilmesi getList
ve manipüle edilebilmesidir.
// 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");
Bu sorunu aşmanın bir yolu, alıcıdan çağrıldığında bir dizinin veya koleksiyonun bir kopyasını döndürmektir:
public List<T> getList() {
// return a copy of the list so the internal state cannot be altered
return new ArrayList(list);
}
Değişmezliğin avantajı nedir?
Değişmezliğin avantajı eşzamanlılık ile birlikte gelir. Değişken nesnelerde doğruluğu korumak zordur, çünkü birden fazla iş parçacığı aynı nesnenin durumunu değiştirmeye çalışıyor olabilir, bu da bazı iş parçacığının aynı nesnenin farklı bir durumunu görmesine neden olabilir, söz konusu nesneye okuma ve yazma zamanlamasına bağlı olarak nesne.
Değişmez bir nesneye sahip olunarak, nesneye bakan tüm iş parçacıkları, değişmez bir nesnenin durumu değişmeyeceğinden, aynı durumu görmesini sağlayabilir.
return Collections.unmodifiableList(list);
salt okunur bir görünüm de döndürebilirsiniz.
final
. Aksi takdirde, bir ayarlayıcı yöntemle (veya diğer tür değiştirme yöntemleri ve değiştirilebilir alanlar) genişletilebilir.
final
Sınıfın yalnızca private
alanlara sahip olması gerekmez , çünkü alt sınıflardan erişilemezler.
Halihazırda verilen cevaplara ek olarak, gözden kaçırılması kolay bazı ayrıntılar (örn. Savunma kopyaları) olduğundan, Etkili Java, 2. Baskı'da değişmezlik hakkında okumanızı tavsiye ederim. Artı, Etkili Java 2. Baskı. her Java geliştiricisi için mutlaka okunması gereken bir kitaptır.
Bir sınıfı böyle değişmez kılarsınız:
public final class Immutable
{
private final String name;
public Immutable(String name)
{
this.name = name;
}
public String getName() { return this.name; }
// No setter;
}
Bir Java sınıfını değiştirilemez kılmak için gerekenler şunlardır:
final
(Böylece alt sınıflar oluşturulamaz)final
(Nesne oluşturduktan sonra değerini değiştirememek için) olarak bildirilmelidir.Değişmez sınıflar kullanışlıdır çünkü
- İş parçacığı açısından güvenlidirler.
- Ayrıca tasarımınız hakkında derinlemesine bir şey ifade ediyorlar: "Bunu değiştiremezsiniz.", Uygulandığı zaman, tam da ihtiyacınız olan şeydir.
Değişmezlik esas olarak iki şekilde elde edilebilir:
final
yeniden atamayı önlemek için örnek özniteliklerini kullanmaDeğişmezliğin avantajları, bu nesneler üzerinde yapabileceğiniz varsayımlardır:
Immutable sınıflar, somutlaştırıldıktan sonra değerleri yeniden atayamaz. Yapıcı, özel değişkenlerine değerler atar. Nesne boş olana kadar, ayarlayıcı yöntemlerin kullanılamaması nedeniyle değerler değiştirilemez.
değişmez olmak için aşağıdakileri tatmin etmelidir,
/**
* 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;
}
}
Advanteges: Değişmez nesneler, ölene kadar başlatılmış değerlerini içerir.
Değişmez sınıf , nesneleri oluşturulduktan sonra değiştirilemeyen .
Değişmez sınıflar için yararlıdır
Misal
String Sınıfı
Kod Örneği
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;
}
}
Bir Java sınıfı nasıl değişmez hale getirilebilir?
JEP 359'a sahip JDK 14+ 'den " records
" kullanabiliriz . Immutable sınıfı yaratmanın en basit ve acelesiz yoludur.
Bir kayıt sınıfı, kayıt için bir açıklama sağlayan ve kayıt olarak bilinen sabit bir alan kümesi için yüzeysel olarak değişmez , şeffaf bir taşıyıcıdır . Her bir yol açmaktadır sağlanan değeri ve bir tutan alanıncomponents
state
component
final
accessor
ve değeri almak için yöntemi ortaya çıkarır. Alan adı ve erişimci adı, bileşenin adıyla eşleşir.
Değişmez bir dikdörtgen oluşturma örneğini düşünelim
record Rectangle(double length, double width) {}
Herhangi bir kurucu bildirmeye gerek yok, equals & hashCode yöntemlerini uygulamaya gerek yok. Sadece herhangi bir Kayıt bir ada ve bir durum açıklamasına ihtiyaç duyar.
var rectangle = new Rectangle(7.1, 8.9);
System.out.print(rectangle.length()); // prints 7.1
Nesne oluşturma sırasında değeri doğrulamak isterseniz, yapıcıyı açıkça bildirmemiz gerekir.
public Rectangle {
if (length <= 0.0) {
throw new IllegalArgumentException();
}
}
Kaydın gövdesi, statik yöntemleri, statik alanları, statik başlatıcıları, oluşturucuları, örnek yöntemlerini ve iç içe geçmiş türleri bildirebilir.
Örnek Yöntemleri
record Rectangle(double length, double width) {
public double area() {
return this.length * this.width;
}
}
statik alanlar, yöntemler
Durum bileşenlerin bir parçası olması gerektiğinden, kayıtlara örnek alanları ekleyemiyoruz. Ancak statik alanlar ve yöntemler ekleyebiliriz:
record Rectangle(double length, double width) {
static double aStaticField;
static void aStaticMethod() {
System.out.println("Hello Static");
}
}
değişmezliğe ihtiyaç nedir ve bunu kullanmanın herhangi bir avantajı var mı?
Önceden gönderilen yanıtlar, değişmezlik ihtiyacını haklı çıkarmak için yeterince iyidir ve artıları
Değişmez nesne yapmanın başka bir yolu da Immutables.org kitaplığını kullanmaktır :
Gerekli bağımlılıkların eklendiğini varsayarak, soyut erişimci yöntemleriyle soyut bir sınıf oluşturun. Arayüzlerle ve hatta ek açıklamalarla (@ arayüz) açıklama ekleyerek aynısını yapabilirsiniz:
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();
}
Artık oluşturulan değişmez uygulamayı oluşturmak ve sonra kullanmak mümkündür:
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)
}
}
Değişmez bir sınıf, örnekleri değiştirilemeyen bir sınıftır.
Her durumda bulunan tüm bilgiler, nesnenin ömrü boyunca sabittir, bu nedenle hiçbir değişiklik gözlenemez.
Değişmez sınıfların tasarlanması, uygulanması ve kullanılması değiştirilebilir sınıflardan daha kolaydır.
Bir sınıfı değişmez kılmak için şu beş kurala uyun:
Nesnenin durumunu değiştiren yöntemler sağlamayın
Sınıfın uzatılamayacağından emin olun.
Tüm alanları son hale getirin.
Tüm alanları özel yapın.
Değiştirilebilir bileşenlere özel erişim sağlayın.
Değişmez nesneler, doğaları gereği iş parçacığı açısından güvenlidir; senkronizasyon gerektirmezler.
Değişmez nesneler özgürce paylaşılabilir.
Değişmez nesneler, diğer nesneler için harika yapı taşları oluşturur
@Jack, Sınıfta son alanlara ve ayarlayıcılara sahip olmak sınıfı değişmez yapmaz. final anahtar sözcüğü yalnızca bir değişkenin asla yeniden atanmadığından emin olun. Alıcı yöntemlerinde tüm alanların derin kopyasını döndürmeniz gerekir. Bu, alıcı yönteminden bir nesne aldıktan sonra nesnenin iç durumunun bozulmamasını sağlayacaktır.
Anadili İngilizce olmayan bir konuşmacı olarak, "değişmez sınıf" ın "inşa edilmiş sınıf nesneleri değişmezdir" olmasının ortak yorumundan hoşlanmıyorum; daha ziyade, bunu "sınıf nesnesinin kendisi değişmez" olarak yorumlamaya meyilliyim.
Bununla birlikte, "değişmez sınıf" bir tür değişmez nesnedir. Aradaki fark, faydanın ne olduğunu yanıtlamaktır. Bildiğim kadarıyla immutable sınıfı, nesnelerinin çalışma zamanı davranış değişikliklerini engeller.
Buradaki cevapların çoğu iyidir ve bazıları kurallardan bahsetmiştir ancak bu kurallara neden ve ne zaman uymamız gerektiğini kelimelere dökmenin iyi olduğunu hissediyorum. Öyleyse aşağıdaki açıklamayı veriyorum
Ve elbette bu son değişkenler için Setters'ı kullanmaya çalışırsak, derleyici hata verir.
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;
}
}
Sınıfı 'final' olarak ilan etmemiz gerekiyor mu?
1. Yalnızca ilkel üyelere sahip olmak: Sorunumuz yok Sınıfın yalnızca ilkel üyeleri varsa, sınıfı nihai olarak ilan etmemize gerek yoktur.
2. Nesnelerin üye değişkenler olarak kullanılması: Üye değişkenler olarak nesnelerimiz varsa, bu nesnelerin üyelerini de nihai hale getirmeliyiz. Ağacın derinliklerine inmemiz ve tüm nesneleri / ilkelleri nihai hale getirmemiz gerektiği anlamına gelir ki bu her zaman mümkün olmayabilir. Dolayısıyla geçici çözüm, kalıtımı önleyen sınıfı nihai hale getirmektir. Dolayısıyla, alıcı yöntemlerini geçersiz kılan bir alt sınıf söz konusu değildir.
Lombok'un @Value ek açıklaması, değişmez sınıflar oluşturmak için kullanılabilir. Aşağıdaki kod kadar basit.
@Value
public class LombokImmutable {
int id;
String name;
}
Lombok'un sitesindeki belgelere göre:
@Value, @Data'nın değişmez varyantıdır; varsayılan olarak tüm alanlar özel ve nihai yapılır ve ayarlayıcılar oluşturulmaz. Sınıfın kendisi de varsayılan olarak nihai hale getirilir, çünkü değişmezlik bir alt sınıfa zorlanabilecek bir şey değildir. @Data gibi, yararlı toString (), equals () ve hashCode () yöntemleri de oluşturulur, her alan bir alıcı yöntemi alır ve her argümanı kapsayan bir kurucu (alan bildiriminde başlatılan son alanlar hariç) de oluşturulur .
record
kullanılabilirken neden Lombok kullanılsın?