Java’da Değişmez (Immutable) Sınıf Nasıl Oluşturulur? 💎
🧠 Bu Rehberde Ne Öğreneceksiniz?
Bu rehber, Java’da immutable (değiştirilemez) sınıfların nasıl oluşturulacağını öğretir.
Immutable sınıflar, nesne oluşturulduktan sonra durumu değişmeyen yapılardır.
Bu, çoklu iş parçacığı (thread-safe) ortamlarda veri bütünlüğünü korur, kodu daha öngörülebilir hale getirir ve hata riskini azaltır.
⚙️ Immutable Sınıf Oluşturma Kuralları
Java’da tam anlamıyla değişmez bir sınıf oluşturmak için aşağıdaki adımları izleyin:
| Adım | Açıklama |
|---|---|
1. Sınıfı final olarak tanımla | Miras alınmasını (extends) engeller, alt sınıflar davranışı değiştiremez. |
2. Alanları private final yap | Dışarıdan erişimi engeller ve sadece bir kez atamaya izin verir. |
| 3. Setter metodu ekleme | Değer değiştiren metotlar (mutator) olmamalıdır. |
| 4. Yapıcı metotta değerleri ata | Tüm alanlar constructor içinde tanımlanmalıdır. |
| 5. Derin kopya (deep copy) kullan | Koleksiyon veya değişebilir nesneler için hem constructor’da hem getter’da kopyalama yapılmalıdır. |
🧱 Derin Kopya (Deep Copy) Örneği
import java.util.HashMap;
public final class AyarlarNesnesi {
private final int id;
private final String isim;
private final HashMap<String, String> ayarMap;
public AyarlarNesnesi(int i, String n, HashMap<String, String> hm) {
System.out.println("Nesne başlatılırken Derin Kopya yapılıyor...");
this.id = i;
this.isim = n;
this.ayarMap = new HashMap<>(hm); // Derin kopya
}
public int getId() { return id; }
public String getIsim() { return isim; }
// Güvenli getter: orijinal map yerine kopya döndür
public HashMap<String, String> getAyarMap() {
return (HashMap<String, String>) ayarMap.clone();
}
}
🧩 Bu yapı, dışarıdan Map üzerinde yapılan değişikliklerin iç durumu etkilemesini önler.
🧪 Test: Derin Kopyanın Farkı
public static void main(String[] args) {
HashMap<String, String> h1 = new HashMap<>();
h1.put("1", "ilk");
AyarlarNesnesi nesne = new AyarlarNesnesi(10, "rabisu", h1);
System.out.println("Orijinal Map: " + nesne.getAyarMap());
h1.put("2", "ikinci"); // dış map değişti
System.out.println("Dış Map değişti. İç Map: " + nesne.getAyarMap());
HashMap<String, String> kopya = nesne.getAyarMap();
kopya.put("3", "yeni");
System.out.println("Getter kopyası değişti. İç Map: " + nesne.getAyarMap());
}
🧩 Sonuç: İçerideki ayarMap sabit kalır — değişmezlik korunur.
⚠️ Sığ Kopya (Shallow Copy) Sorunu
public AyarlarNesnesi(int i, String n, HashMap<String, String> hm) {
this.id = i;
this.isim = n;
this.ayarMap = hm; // ⚠️ Tehlikeli: Dış referans korunur
}
Eğer bu şekilde doğrudan atarsanız, dışarıdan yapılan değişiklikler sınıfın iç durumunu etkiler. Sonuç: Sınıf artık immutable değildir.
🧩 Arayüz Tasarım Hataları ve Çözümleri
| Hata Tipi | Risk | Çözüm |
|---|---|---|
| Getter’da canlı referans döndürmek | Dışarıdan iç Map değiştirilebilir. | return new HashMap<>(map); veya map.clone() kullanın. |
| Constructor’da kopya almamak | Dış girdi iç durumu etkiler. | this.map = new HashMap<>(inputMap); |
| Collections.unmodifiableMap kullanmak | Map sabit ama iç değerler değişebilir. | İç elemanların da immutable olmasına dikkat edin. |
📚 Kütüphane Desteği: Immutable Koleksiyonlar
🧩 Guava ImmutableMap
import com.google.common.collect.ImmutableMap;
public final class GuavaExample {
private final ImmutableMap<String, String> config;
public GuavaExample(Map<String, String> input) {
this.config = ImmutableMap.copyOf(input); // Tek adımda immutable kopya
}
}
✅ Guava ImmutableMap, ImmutableList, ImmutableSet gibi derin olarak değiştirilemez koleksiyonlar sunar. Getter’da kopyalama gerekmez.
⚙️ Vavr Koleksiyonları (Fonksiyonel Yaklaşım)
import io.vavr.collection.Map;
import io.vavr.collection.HashMap;
public final class VavrExample {
private final Map<String, String> settings;
public VavrExample(Map<String, String> input) {
this.settings = input; // Vavr koleksiyonları zaten immutable’dır
}
}
✅ Fonksiyonel programlama tarzı için uygundur. Her değişiklik yeni bir nesne oluşturur, orijinali etkilemez.
🧩 Derin Kopya vs Sığ Kopya Karşılaştırması
| Tür | Açıklama | Örnek Davranış |
|---|---|---|
| Derin Kopya (Deep Copy) | Her seviyedeki veri kopyalanır. | new HashMap<>(orijinal) |
| Sığ Kopya (Shallow Copy) | Sadece referans kopyalanır. | this.map = orijinal |
🧩 Ortak Hatalar Tablosu
| Durum | Hatalı Kod | Çözüm |
|---|---|---|
Arrays.asList() | Arrays.asList("A","B").add("C"); ❌ | new ArrayList<>(Arrays.asList("A","B")) ✅ |
List.of() (Java 9+) | List.of("X","Y").add("Z"); ❌ | new ArrayList<>(List.of("X","Y")) ✅ |
💬 Sıkça Sorulan Sorular (SSS)
- final anahtar kelimesi değişmezlik sağlar mı?
Hayır. final yalnızca referansın yeniden atanmasını engeller. İçerik hâlâ değişebilir olabilir.
- String neden immutable’dır?
String içindeki karakter dizisini dışarıya sızdırmaz ve değiştirici metot içermez.
- Değişmezlik çoklu iş parçacığı için neden önemlidir?
Immutable nesneler kilit (lock) gerekmeden paylaşılabilir. Bu da thread-safe bir yapı sağlar.
- Değişmez sınıflar performansı düşürür mü?
Kopyalama maliyeti vardır, ancak basitleşen mantık ve artan güvenlik bu farkı dengeler.
- Değer değişikliği nasıl yapılır?
Yeni bir nesne oluşturulur. Örneğin:
String s2 = s1.toUpperCase(); // s1 değişmez, s2 yenidir.
🚀 Sonuç
Immutable sınıflar, Java’da güvenli, hataya dayanıklı ve thread-safe uygulamalar geliştirmenin temelidir. final alanlar, derin kopyalama ve immutable koleksiyonlar kullanarak kodunuzu daha öngörülebilir hale getirebilirsiniz.
💡 Bu prensipleri, Rabisu Bulut platformunda oluşturduğunuz Java tabanlı DTO veya yapılandırma sınıflarında hemen uygulayabilirsiniz.