Mutable ve Immutable Nesneler
Immutable nesnelerin özellikleri, ne zaman tercih edilmeli, avantaj ve dezavantajları örneklerle.
Tanım
- Oluşturulduktan sonra iç durumları (state) değiştirilebilen nesnelere mutable denir. Dolayısıyla iç durumu değiştirilemeyen nesnelere de immutable denilir.
- Java’da en klasik örnek
String(immutable) ileStringBuilder(mutable) dır.
Immutable Objelerin Özellikleri
- 🧵 Thread-safe’tir; birden fazla thread kolaylıkla paylaşabilir.
- 🤝 Durumu değişmediği için güvenli bir şekilde paylaşılabilir, paylaşım sırasında defensive copy gerekmez.
- 🔑 Hash değeri değişmediği için
HashMap/Setanahtarı olarak güvenle kullanılır. - 📥 Immutable objeler yalnızca
getmetotları sunar. - 🆕 Immutable bir nesnenin durumunu değiştirmeye yönelik bir girişim yeni bir nesne üretir. Dolayısıyla sık güncellenmesi gereken bir nesne immutable olmamalıdır. Mesela
Stringbir değişken bir döngü içerisinde sürekli güncellenecek iseStringBuilderkullanmak daha doğru olacaktır.
⚠️ Nüans: “Defensive copy gerekmez” ifadesi nesneyi paylaşırken geçerlidir (çünkü nesne zaten değişemez). Ancak immutable bir sınıfın iç alanı mutable bir nesne ise (ör.
List), bu alanı constructor’da alırken ve getter’da dönerken defensive copy yapılmalıdır; aksi halde dışarıdan içerik değiştirilebilir.
Kullanım Senaryosu
User Story
Bir kullanıcı hesabından fatura ödemesi yapmak istiyor; böylece bakiyesinden ödeme tutarı ve işlem ücreti düşülecek ve doğru kalan bakiyeyi görebilecek.
Kabul kriteri: Bakiye yetersizse işlem reddedilmeli ve orijinal bakiye hiç değişmemeli.
Senaryo
Ahmet’in hesabında 100.00 TL var. 84.50 TL’lik fatura + 1.00 TL işlem ücreti ödeyecek. Toplam 85.50 TL. İşlem sonrası bakiye 14.50 TL olmalı.
BigDecimal immutable olduğu için, hesaplama sırasında orijinal bakiye asla bozulmaz. Bu, failure atomicity güvencesini sağlar: başarısız bir işlem sonrasında bile nesne, işlem çağrılmadan önceki tutarlı halinde kalır.
public class PaymentService {
// Bakiye ve tutarlar BigDecimal (immutable) — para hesabında double ASLA kullanılmaz
public BigDecimal payBill(BigDecimal balance, BigDecimal billAmount, BigDecimal fee) {
BigDecimal totalCost = billAmount.add(fee); // yeni nesne: 85.50
// Kontrol: bakiye yeterli mi? (compareTo, çünkü equals ölçeğe duyarlıdır)
if (balance.compareTo(totalCost) < 0) {
throw new IllegalStateException("Yetersiz bakiye");
// balance HİÇ değişmedi -> hesap tutarlı kaldı (failure atomicity)
}
BigDecimal newBalance = balance.subtract(totalCost); // yeni nesne: 14.50
return newBalance;
}
}
Çağrım:
var service = new PaymentService();
BigDecimal balance = new BigDecimal("100.00");
BigDecimal bill = new BigDecimal("84.50");
BigDecimal fee = new BigDecimal("1.00");
BigDecimal result = service.payBill(balance, bill, fee);
System.out.println(balance); // 100.00 -> ORİJİNAL DEĞİŞMEDİ
System.out.println(result); // 14.50 -> yeni bakiye
Buradaki Kilit Noktalar
- Orijinal bozulmaz.
add/subtractyeni nesne döndürdüğü içinbalanceher satırda hâlâ 100.00. İşlemin ortasında hata çıksa bile hesap “yarı düşülmüş” hatalı bir durumda kalmaz. - Yanlışlıkla değiştirme (aliasing) riski yok. Aynı
balancenesnesi başka bir servise/thread’e geçse bile, kimse onun içeriğini değiştiremez. Mutable olsaydı bir yerdebalance.setValue(...)yapıldığında her yer etkilenirdi — buna aliasing bug denir.
Mutable Olsaydı Ne Olurdu? (karşılaştırma)
💡 Not:
BigDecimalgerçekte immutable’dır. AşağıdakiMoneysınıfı, mutable davranışı göstermek için kullanılan farazi bir örnektir.
// Mutable (TEHLİKELİ senaryo — farazi)
Money balance = new Money("100.00");
balance.subtract(totalCost); // nesnenin İÇİ değişir -> balance artık 14.50
// Sorun: aynı balance nesnesini paylaşan başka bir yer varsa,
// onun da değeri habersizce değişti. Ayrıca subtract sırasında hata olursa
// nesne yarı-değişmiş, tutarsız durumda kalabilir.
Özet Tablo: Mutable vs Immutable
Kaynak: GeeksforGeeks — “Mutable and Immutable in Java”.
| Mutable | Immutable |
|---|---|
| Nesnenin değeri initialization sonrası değiştirilebilir. | Nesnenin değeri initialization sonrası değiştirilemez. |
| State (durum) değiştirilebilir. | State, oluşturulduktan sonra sabit kalır. |
| Yeni nesne oluşmaz. | Nesnenin değerini değiştirmek yeni bir nesne oluşturur. |
| Nesneyi değiştirmek için metotlar sunar. | Nesne değerini değiştirmek için metot sunmaz. |
get() ve set() metotlarını destekler. |
Nesne değerini almak için yalnızca get() metodunu destekler. |
| Thread-safe olabilir ya da olmayabilir. | Immutable nesneler doğası gereği thread-safe’tir. |
| Mutable bir sınıf oluşturmanın temelleri: alanları değiştiren metotlar, getter’lar ve setter’lar. | Immutable bir sınıf oluşturmanın temelleri: final class, private alanlar ve final mutable nesneler. |
Mutable & Immutable Nesnelerin Avantajları ve Kullanımları
Kaynak: GeeksforGeeks — “Mutable and Immutable in Java”.
| Mutable Nesnelerin Avantajları | Immutable Nesnelerin Avantajları |
|---|---|
| Esneklik (Flexibility): Mutable sınıf nesneleri durumlarının değiştirilmesine izin verir; bu, dinamik değişiklikler için kullanışlıdır. | Thread Safety: Immutable nesneler, durumları değişemediği için senkronizasyon olmadan thread’ler arasında paylaşılabilir. |
| Performans: Yeni nesne oluşturmadan doğrudan değiştirilebildikleri için genellikle iyi performans gösterirler. | Güvenlik (Security): Immutable nesneler daha güvenlidir; durumları oluşturulduktan sonra kötü niyetle değiştirilemez. |
| Builder Pattern: Mutable nesneler, akıcı (fluent) ve okunabilir nesne oluşturmaya olanak tanıyan builder pattern ile yaygın olarak kullanılır. | Daha Basit Durum Yönetimi: Immutable nesneler, durumları beklenmedik şekilde değişmediği için hata ayıklamayı ve bakımı basitleştirir. |
| Caching: Durumları değişmediği için önbellekleme (caching) mekanizmaları için idealdir; kolayca cache’lenebilirler. | |
| Bellek Yönetimi: Defensive copy gerektirmedikleri için bellek yönetimi daha kolaydır. |
Aynı kategoriden
Java: Pass by Value ve Defensive Copy
Java gerçekten pass-by-value mı? Referansların kopyalanması ve defensive copy ile nesne bütünlüğünü koruma.
JVM Bellek Alanları
Heap, metaspace, stack, PC register: hangi veri nerede durur, thread-safety ile ilişkisi nedir?
JDK, JRE ve JVM Arasındaki Fark
JDK, JRE ve JVM ne işe yarar, birbirini nasıl kapsar? Kısa karşılaştırma tablosuyla.