Skip to main content

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ımAçıklama
1. Sınıfı final olarak tanımlaMiras alınmasını (extends) engeller, alt sınıflar davranışı değiştiremez.
2. Alanları private final yapDışarıdan erişimi engeller ve sadece bir kez atamaya izin verir.
3. Setter metodu eklemeDeğer değiştiren metotlar (mutator) olmamalıdır.
4. Yapıcı metotta değerleri ataTüm alanlar constructor içinde tanımlanmalıdır.
5. Derin kopya (deep copy) kullanKoleksiyon 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 TipiRiskÇözüm
Getter’da canlı referans döndürmekDışarıdan iç Map değiştirilebilir.return new HashMap<>(map); veya map.clone() kullanın.
Constructor’da kopya almamakDış girdi iç durumu etkiler.this.map = new HashMap<>(inputMap);
Collections.unmodifiableMap kullanmakMap 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ürAçı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

DurumHatalı 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)

  1. 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.

  1. String neden immutable’dır?

String içindeki karakter dizisini dışarıya sızdırmaz ve değiştirici metot içermez.

  1. 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.

  1. 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.

  1. 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.