Press "Enter" to skip to content

Effective Java Madde 24: Statik Üye Sınıfları Statik Olmayanlara Tercih Edin

Başka bir sınıfın içinde tanımlanan sınıflara gömülü sınıf (nested class) denir. Gömülü sınıflar sadece içerisinde tanımlı oldukları sınıfa hizmet ederler. Eğer başka bir yerde de faydalı olabileceklerse, kendi başlarına normal bir sınıf olarak tanımlanmaları gerekir. Java’da dört çeşit gömülü sınıf vardır: statik üye sınıflar (static member class), statik olmayan üye sınıflar (nonstatic member class), isimsiz sınıflar (anonymous class) ve yerel sınıflar (local class). Bunların ilki hariç diğerleri aynı zamanda dahili sınıflar (inner class) olarak bilinir. Bu maddede, bunların hangisini ne zaman kullanmamız gerektiğini anlatacağız.

Statik üye sınıflar, gömülü sınıfların en basitidir. İçinde tanımlandıkları sınıfın private bile olsa bütün static üyelerine erişimi olan sıradan bir sınıf olarak görülebilirler. Bu sınıflar, kendisini çevreleyen sınıfın static bir üyesidir ve diğer static üyelerin uymak zorunda olduğu erişebilirlik kurallarına uymak zorundadırlar. private tanımlanırlarsa sadece çevreleyen sınıf içerisinden erişilebilirler.

Statik üye sınıflar, çevreleyen sınıfı kullanan istemciler için bir yardımcı sınıf olarak görevi görebilirler. Örneğin, bir hesap makinasının desteklediği işlemleri tanımlayan bir Enum türü düşünelim. (Madde 34) Operation isimli bu enum türü, Calculator sınıfının içerisinde public ve static bir üye sınıf olarak tanımlanabilir. Calculator istemcileri de, desteklenen işlemlere Calculator.Operation.PLUS ve Calculator.Operation.MINUS şeklinde erişebilir.

Sözdizimsel olarak (syntax) statik ve statik olmayan üye sınıflar arasındaki tek fark, üye sınıf tanımlanırkenstatic anahtar kelimesinin kullanılıp kullanılmamasından ibarettir. Ancak bu iki gömülü sınıf türü birbirlerinden çok farklıdır. Statik olmayan bir üye sınıfın nesnesi, onu çevreleyen sınıfın nesnesiyle dolaylı olarak ilişkilidir. Burada gömülü sınıftaki metotlardan, çevreleyen sınıfın metotları çağrılabilir ve hatta çevreleyen nesnenin this referansına sınıf ismi kullanılarak erişilebilir. (JLS, 15.8.4) Yukarıdaki örnekte Operation statik olmasaydı, Calculator.this şeklinde çevreleyen nesnenin referansına erişebilirdi. Eğer gömülü sınıfın nesnesi onu kapsayan sınıfın nesnesinden bağımsız olarak var olabiliyorsa, bu gömülü sınıf statik üye sınıf olarak tanımlanmak zorundadır. Statik olmayan bir üye sınıfın nesnesini, çevreleyen sınıfın nesnesi olmadan tek başına yaratamayız. Statik olmayan üye sınıf ile onu çevreleyen sınıfın nesneleri arasındaki bağ, çevreleyen sınıf üye sınıftan bir nesne yarattığında kurulur ve sonrasında değiştirilemez. Tahmin edeceğiniz gibi arada kurulması gereken bu ilişki üye sınıf içerisinde yer işgal eder ve nesnenin yaratılmasını geciktirir.

Statik olmayan üye sınıfların kullanım alanlarından bir tanesi Adapter olarak kullanılabilmeleridir. Map gerçekleştirimleri statik olmayan üye sınıfları kullanarak söz konusu Map için bir Collection görünümü oluştururlar ve bunu keySet, entrySet ve values metotlarından döndürürler. Benzer olarak, Set ve List gibi Collection arayüzlerinin gerçekleştirimleri de bu üye sınıfları Iterator yaratmak için kullanırlar:

// Statik olmayan üye sınıfların tipik bir kullanımı 
public class MySet<E> extends AbstractSet<E> {
    ... // sınıfın geri kalanı çıkartılmıştır
    @Override 
    public Iterator<E> iterator() {
        return new MyIterator();
    }

    private class MyIterator implements Iterator<E> { 
        ...
    }
}

Burada onu çevreleyen Set olmadan, Iterator nesnesinin varlığı anlamsız kalacağı için üye sınıfın statik olmaması doğrudur. Ancak, çevreleyen nesneye erişim ihtiyacı olmayan bir üye sınıf tanımlıyorsanız, mutlaka static yapmalısınız. Aksi halde, her nesne onu çevreleyen nesneye gizli bir referans barındıracaktır. Bu referansın tutulması hem zaman hem de bellek kaybına yol açar. Normalde çöp toplayıcı (garbage collector) tarafından serbest bırakılması gereken ana nesne, üye sınıftaki gereksiz referanstan ötürü bellekte tutulmak zorunda kalabilir. (Madde 7) Sonuçta çok kötü sonuçlar doğuran bir bellek sızıntısı (memory leak) ile karşılaşabilirsiniz. Daha da kötüsü, referans üye sınıfta gizli halde olduğu için farketmesi de zor olacaktır.

Çevreleyen sınıf nesnelerinin belli bileşenlerini temsil etmek üzere gizli (private) statik üye sınıflar kullanabilirsiniz. Örneğin anahtar – değer ilişkisi tutan Map nesnelerini düşünelim. Birçok Map gerçekleştirimi içerisindeki her bir girdi (anahtar – değer ikilisi) için bir Entry nesnesi tutar. Bu girdiler Map ile ilişkili olmasına rağmen, Entry içindeki getKey, getValue, setValue gibi metotların onu çevreleyen Map nesnesine erişmesine gerek yoktur. Bu sebeple, gizli statik üye sınıf olarak tanımlanması en doğrusu olacaktır. Burada static niteleyicisini koymayı unutursanız, Map yine de çalışacaktır ama Entry içindeki gereksiz Map referansı yüzünden bellek ve zaman kaybı olacaktır.

Statik ve statik olmayan üye sınıflar arasında tercih yapmak, bu üye sınıf eğer public veya protected düzeyinde erişebilirliğe sahipse çok daha önemli bir hal almaktadır. Çünkü bu durumda üye sınıf dışarı açılan API’ın bir parçasıdır ve sonradan üye sınıfı static yapmak geriye uyumluluğu (backwards compatibility) bozmadan mümkün olmayacaktır.

Adından da anlaşılacağı üzere, isimsiz sınıfların (anonymous class) isimleri yoktur. İçinde tanımlandıkları sınıfın bir üyesi değildirler. Kod işletim sırası onlara geldiği anda hem tanımlanır hem de yaratılırlar. Java’da expression yazılabilen her yerde bulunabilirler. İsimsiz sınıflar sadece statik olmayan bir ortamda (nonstatic context) tanımlandıkları zaman bir çevreleyen nesneye sahip olurlar. Bununla beraber, statik bir ortamda (static context) tanımlansalar dahi ilkel türler (primitive) veya String türünde sabit (final) değişkenler haricinde statik üyeler tanımlayamazlar. (JLS, 4.12.4)

İsimsiz sınıfların kullanımını kısıtlayan birçok etken vardır. İsimsiz sınıfların nesnelerini tanımlandıkları nokta haricinde bir yerde yaratamazsınız. İsimleri olmadığı için instanceof operatörü ile kullanamazsınız, hatta isim gerektiren hiçbir yerde kullanamazsınız. Bu sınıflar birden fazla arayüzü uygulayamazlar, aynı anda hem bir sınıfı kalıtıp hem de bir arayüzü uygulayamazlar. İstemcileri, isimsiz sınıfın geçersiz kıldığı metotlar haricinde hiçbir metodu çağıramaz. Aynı zamanda kod satır sayısı olarak da kısa tutulmalıdırlar – en fazla on satır civarı – aksi taktirde okunabilirlikleri çok zayıf olur.

Lambda fonksiyonları Java’ya eklenmeden önce, isimsiz sınıflar küçük fonksiyon nesneleri yaratmak için tercih edilirdi, ancak şimdi lambda fonksiyonları bu boşluğu doldurmaktadır. (Madde 42) İsimsiz sınıflar statik fabrika metotlarını gerçekleştirmek için de sıklıkla kullanılır. Bunun örneği için Madde 20’deki intArrayAsList metoduna bakabilirsiniz.

Yerel sınıflar, yazının başında belirtilen dört gömülü sınıf türünden en az kullanılanıdır. Yerel bir sınıf, yerel değişlenlerin tanımlandığı her yerde tanımlanabilir ve geçerlilik alanları (scope) yerel değişkenlerle aynıdır. Bununla beraber diğer gömülü sınıf türlerinden farklı özellikler almışlardır. Üye sınıflar gibi isimleri vardır ve tekrar tekrar kullanılabilirler. İsimsiz sınıflar gibi, sadece statik olmayan bir ortamda (nonstatic context) tanımlandıkları zaman çevreleyen nesnelerinden bahsedilebilir ve statik herhangi bir üye tanımlayamazlar. Yine isimsiz sınıflar gibi uzun olmaları halinde okunabilirliği olumsuz etkilerler.

Özetle, Java’da birbirlerinden farklı kullanım alanları olan dört çeşit gömülü sınıf türü vardır. Gömülü sınıf eğer birden fazla metot tarafından kullanılacaksa veya bir metodun içine sığamayacak kadar büyükse o zaman üye sınıf kullanın. Eğer üye sınıf nesnelerinin çevreleyen nesneye erişmesi gerekiyorsa statik olmayan, gerekmiyorsa da statik üye sınıf kullanın. Gömülü sınıf sadece bir metoda ait olduğu durumda, eğer sınıfın nesnesini tek bir yerde yaratmanız gerekiyorsa ve sınıfın karakteristiğini belirleyebilecek mevcut bir tür varsa isimsiz sınıf kullanın, aksi taktirde yerel sınıf kullanın.

Share

2 Comments

Leave a Reply

%d bloggers like this: