Anonim Sınıflar (Anonymous Classes)
Bazen bir arayüzü veya soyut sınıfı yalnızca bir kez, sadece o anda kullanmak için uygulamak istersin; ona ayrı bir isim/dosya vermek gereksizdir. Anonim sınıflar tam da bunun içindir: bir sınıfı tanımladığın yerde, i…
Anonim Sınıflar (Anonymous Classes)
Bazen bir arayüzü veya soyut sınıfı yalnızca bir kez, sadece o anda kullanmak için uygulamak istersin; ona ayrı bir isim/dosya vermek gereksizdir. Anonim sınıflar tam da bunun içindir: bir sınıfı tanımladığın yerde, isim vermeden oluşturursun. Java 8 öncesinin "her yerde" kullandığı bu yapı, bugün çoğu yerde lambda'lara dönüşmüş olsa da hâlâ önemli bir yere sahiptir.
Söz dizimi: tanımla ve anında oluştur
Anonim sınıf, new Tip() { ... } biçimindedir: belirtilen arayüzü uygular veya sınıfı
uzatır, gövdeyi yazar ve aynı anda bir nesne oluşturur:
Selamlayici s = new Selamlayici() { // arayüzü anında uygula @Override public String selamla(String ad) { return "Sayın " + ad; } }; Hayvan h = new Hayvan() { // abstract sınıfı anında uzat @Override String ses() { return "Hav"; } };
Klasik kullanımları: Comparator, Runnable, olay dinleyicileri (event listeners), Iterator.
Örnek 1 (./Ornek1.java) bir arayüzü, bir abstract sınıfı ve bir Comparator'ı anonim sınıfla
uygular.
Anonim sınıf mı, lambda mı?
Java 8'den beri, tek soyut metotlu (functional) arayüzler için lambda çok daha kısadır ve tercih edilir:
// Anonim sınıf Function<Integer,Integer> kare = new Function<>() { public Integer apply(Integer x) { return x * x; } }; // Lambda — aynısı Function<Integer,Integer> kare = x -> x * x;
Ama her durumda lambda kullanılamaz. Anonim sınıf gerekir eğer:
- Bir abstract sınıfı uzatman gerekiyorsa (lambda yalnızca tek-metotlu arayüzler içindir),
- Arayüzde birden çok metot varsa,
- Ek alan/durum (state) tutman gerekiyorsa,
this'in anonim nesneyi göstermesi gerekiyorsa.
thisfarkı (önemli incelik): Anonim sınıftathis, anonim nesnenin kendisidir. Lambda'da isethis, çevreleyen nesnedir (lambda yeni bir kapsam/thisaçmaz). Örnek 2 (./Ornek2.java) hem lambda-anonim karşılaştırmasını hem de buthisfarkını gösterir.
Değişken yakalama
Anonim sınıflar (ve lambda'lar), çevreleyen metodun effectively final (bir kez atanıp değişmeyen) yerel değişkenlerini "yakalar" (capture). Bu yüzden bir döngü değişkenini doğrudan değiştirip yakalamaya çalışmak derleme hatası verir; bunun yerine final bir kopya kullanılır.
Nerede karşına çıkar?
- GUI/olay yönetimi: "butona tıklanınca şunu yap" (Swing/AWT — ileride).
- Eşzamanlılık:
new Thread(new Runnable() { ... })(bugünnew Thread(() -> ...)). - Sıralama/filtreleme:
Comparator,Predicate(bugün lambda). - Geri çağırma (callback): tek seferlik davranış enjeksiyonu.
Özet
Anonim sınıfların bir arayüzü/abstract sınıfı isim vermeden, tek kullanımlık biçimde uygulamayı
sağladığını (Örnek 1); functional arayüzlerde lambda'nın daha kısa olduğunu ama abstract sınıf
uzatma, çok metot, durum ve this davranışı gerektiğinde anonim sınıfın şart olduğunu (Örnek 2)
öğrendik. Anonim sınıflar, iç sınıfların en yoğun ve en pratik biçimidir. Sırada, tüm uygulamada
tek bir örnek garantileyen klasik desen: Singleton sınıf.
▶ Kod Örnekleri(2)
Ornek1
çalıştırılabilir1// Ornek1: Anonim sınıf — bir arayüzü/abstract sınıfı isim vermeden, ANINDA uygulamak.
2// Çalıştırma: java Ornek1.java
3import java.util.ArrayList;
4import java.util.Comparator;
5import java.util.List;
6
7public class Ornek1 {
8
9 interface Selamlayici { String selamla(String ad); }
10
11 abstract static class Hayvan {
12 abstract String ses();
13 void tanit() { System.out.println("Bu hayvan: " + ses()); }
14 }
15
16 public static void main(String[] args) {
17 // 1) Bir arayüzü anonim sınıfla uygula (tek kullanımlık).
18 Selamlayici resmi = new Selamlayici() {
19 @Override public String selamla(String ad) { return "Sayın " + ad + ", hoş geldiniz."; }
20 };
21 System.out.println(resmi.selamla("Ada"));
22
23 // 2) Bir abstract sınıfı anonim sınıfla uzat (lambda BUNU yapamaz; sınıf gerekir).
24 Hayvan kopek = new Hayvan() {
25 @Override String ses() { return "Hav hav"; }
26 };
27 kopek.tanit();
28
29 // 3) Comparator'ı anonim sınıfla (klasik kullanım).
30 List<String> sehirler = new ArrayList<>(List.of("İzmir", "Ankara", "Adana"));
31 sehirler.sort(new Comparator<String>() {
32 @Override public int compare(String a, String b) { return a.compareTo(b); }
33 });
34 System.out.println("Sıralı: " + sehirler);
35
36 System.out.println("""
37
38 --- Anonim sınıf ---
39 İsmi olmayan, BİR KEZ kullanılacak bir sınıfı tanımladığın yerde oluşturursun.
40 'new Tip() { ... }' söz dizimi: arayüzü uygular VEYA sınıfı uzatır + nesneyi yaratır.
41 Tek soyut metotlu arayüzlerde genelde LAMBDA daha kısadır (sonraki örnek);
42 ama abstract SINIF uzatmak veya birden çok metot gerektiğinde anonim sınıf şarttır.""");
43 }
44}Ornek2
çalıştırılabilir1// Ornek2: Anonim sınıf vs lambda — ne zaman hangisi?
2// Çalıştırma: java Ornek2.java
3import java.util.function.Function;
4
5public class Ornek2 {
6
7 public static void main(String[] args) {
8 // Tek soyut metotlu (functional) arayüz: anonim sınıf VE lambda ile yazılabilir.
9
10 // a) Anonim sınıf (uzun)
11 Function<Integer, Integer> kareAnonim = new Function<Integer, Integer>() {
12 @Override public Integer apply(Integer x) { return x * x; }
13 };
14
15 // b) Lambda (aynı şey, kısa) — functional interface'lerde TERCİH EDİLEN.
16 Function<Integer, Integer> kareLambda = x -> x * x;
17
18 System.out.println("anonim: " + kareAnonim.apply(5) + ", lambda: " + kareLambda.apply(5));
19
20 // 'this' farkı:
21 // - Anonim sınıfta 'this' ANONİM nesneyi gösterir.
22 // - Lambda'da 'this' ÇEVRELEYEN nesneyi gösterir (lambda ayrı bir kapsam açmaz).
23 new Ornek2().thisFarki();
24
25 System.out.println("""
26
27 --- Anonim sınıf mı, lambda mı? ---
28 LAMBDA kullan (kısa, okunaklı) eğer:
29 - hedef TEK soyut metotlu bir functional interface ise.
30 ANONİM SINIF kullan eğer:
31 - bir ABSTRACT SINIFI uzatman gerekiyorsa, VEYA
32 - birden çok metot/ek alan (durum) gerekiyorsa, VEYA
33 - 'this' anonim nesneyi göstermeli ise.
34 Tarihsel olarak anonim sınıflar yaygındı; Java 8 lambda'larla çoğu yerde sadeleşti.""");
35 }
36
37 void thisFarki() {
38 Runnable r = () -> System.out.println("\nlambda içindeki this -> çevreleyen sınıf: "
39 + this.getClass().getSimpleName());
40 r.run();
41 }
42}