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.
[…] fonksiyon nesneleri yaratmanın birincil yolu isimsiz sınıflar (anonymous class) kullanmaktı (Madde 24). Aşağıda bir sıralama fonksiyonu kullanarak bir grup String nesnesini karakter sayısına […]
[…] sağlamaktır. Bu yardımcı sınıflar genelde statik üye sınıf olarak tanımlanırlar. (Madde 24) Eğer belli bir grup parametre sıklıkla tekrar ediyorsa ve bunlar mantıksal olarak bir […]