1. Anonim siniflərə daha dərindən baxış
Anonim sinif — adı olmayan alt sinif və ya interfeys reallaşdırmasıdır və birbaşa istifadə yerində yaradılır. Lambda-lar (Java 8) meydana çıxmazdan əvvəl bu, interfeysi və ya abstrakt sinfi “birdəfəlik” reallaşdırmağın ən rahat yolu idi.
Klassik nümunə:
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Anonim sinifdən salam!");
}
};
r.run();
Burada biz Runnable interfeysini elan edib dərhal reallaşdırdıq — ayrıca fayl və sinif adı olmadan. Belə reallaşdırmalar hadisə emalçıları, komparatorlar, axınlar və digər tapşırıqlarda davranışı tez “taxmaq” lazım olduqda tez-tez istifadə edilirdi.
Əgər lambda “yerindəcə ifadə”dirsə, anonim sinif “adı olmayan kiçik aktyor” kimidir: epizodik rolu oynayır və yox olur.
2. Lambda ifadələri ilə müqayisə
Sintaksis
Anonim sinif:
Comparator<String> comp = new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
};
Lambda ifadəsi:
Comparator<String> comp = (a, b) -> a.length() - b.length();
Fərq aydındır: lambda daha yığcamdır — sadə əməl olduqda tipləri, metod adını və artıq figur mötərizələri açıq-aydın yazmağa ehtiyac yoxdur.
Funksionallıq
- Anonim sinif — tamhüquqlu obyekt-dir. Sahələr elan etmək, əlavə metodlar yazmaq, Object metodlarını (toString, equals və s.) yenidən təyin etmək olar.
- Lambda ifadəsi — funksional interfeysin bir abstrakt metodunun reallaşdırılmasıdır. İçəridə öz sahələrinizi və ya əlavə metodlar elan etmək olmaz.
Nə zaman hansını seçmək?
- Lambda — funksional interfeysin bir metodunu qısa şəkildə reallaşdırmaq lazım olduqda.
- Anonim sinif — aşağıdakı hallarda:
- bir neçə metodu reallaşdırmaq lazım olanda (məsələn, abstrakt sinif);
- vəziyyəti saxlamaq üçün sahələr (field-lər) elan etmək lazım olanda;
- Object metodlarını yenidən təyin etmək lazım olanda (məsələn, toString);
- irsi əlaqələrə/girişə aid xüsusiyyətlərdən istifadə etmək lazım olanda (məsələn, super sinifin qorunan üzvlərinə çıxış).
3. Görünürlük sahəsi və açar söz this
Burada tez-tez rast gəlinən bir tələ var:
- anonim sinifdə this anonim sinifin öz instansına işarə edir;
- lambda ifadəsində this lambda-nın elan edildiyi xarici sinifə işarə edir.
Nümunə: davranışı müqayisə edək
public class Outer {
String name = "Xarici sinif";
void test() {
Runnable anon = new Runnable() {
String name = "Anonim sinif";
@Override
public void run() {
System.out.println(this.name); // "Anonim sinif"
}
};
Runnable lambda = () -> System.out.println(this.name); // "Xarici sinif"
anon.run();
lambda.run();
}
}
Nəticə:
Anonim sinif
Xarici sinif
Anonim sinifdə this elə həmin anonim sinifə işarə edir (onun name sahəsi götürülür). Lambda-da this — bu, Outer-dir.
4. Anonim siniflərdən nə vaxt istifadə etməli?
Bir neçə metodu reallaşdırmaq lazım olduqda
Lambda yalnız funksional interfeyslərlə (dəqiq bir abstrakt metod) işləyir. Əgər interfeys/abstrakt sinif bir neçə metodun reallaşdırılmasını tələb edirsə — anonim sinif lazımdır.
abstract class Animal {
abstract void say();
abstract void jump();
}
Animal cat = new Animal() {
@Override
void say() {
System.out.println("Miyav!");
}
@Override
void jump() {
System.out.println("Tullan!");
}
};
Vəziyyəti (sahələri) saxlamaq lazım olduqda
Runnable r = new Runnable() {
int counter = 0;
@Override
public void run() {
counter++;
System.out.println("Çağırıldı " + counter + " dəfə");
}
};
r.run(); // Çağırıldı 1 dəfə
r.run(); // Çağırıldı 2 dəfə
Object metodlarını yenidən təyin etmək tələb olunduqda
Comparator<String> comp = new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
@Override
public String toString() {
return "Sətir uzunluğuna görə komparator";
}
};
System.out.println(comp); // Sətir uzunluğuna görə komparator
5. Nümunələr: Comparator və Runnable — lambda vs anonim sinif
Sətirlərin uzunluğa görə çeşidlənməsi
Anonim sinif:
List<String> words = Arrays.asList("pişik", "fil", "siçan", "pələng");
words.sort(new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
});
System.out.println(words);
Lambda ifadəsi:
List<String> words = Arrays.asList("pişik", "fil", "siçan", "pələng");
words.sort((a, b) -> a.length() - b.length());
System.out.println(words);
Nəticə eynidir, amma lambda ilə kod daha qısa və daha oxunaqlıdır.
Runnable: axının işə salınması
Anonim sinif:
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Anonim sinif vasitəsilə axın");
}
});
t1.start();
Lambda ifadəsi:
Thread t2 = new Thread(() -> System.out.println("Lambda vasitəsilə axın"));
t2.start();
Sahələri olan anonim sinif
Runnable r = new Runnable() {
int count = 0;
@Override
public void run() {
count++;
System.out.println("Çağırıldı " + count + " dəfə");
}
};
r.run(); // Çağırıldı 1 dəfə
r.run(); // Çağırıldı 2 dəfə
Lambda-da bunu etmək olmur — sahə elan etmək imkanı yoxdur.
6. Xüsusiyyətlər: görünürlük sahəsi, dəyişənlər və final
Həm anonim siniflərdə, həm də lambda ifadələrində xarici metodun lokal dəyişənlərindən yalnız onlar final və ya “effektiv final” (ilkin dəyərdən sonra dəyişdirilmir) olduqda istifadə etmək olar. Amma adlarla bağlı bir nüans var:
- anonim sinifdə xarici sahə ilə eyni adda dəyişən elan etmək olar (“kölgələmə”);
- lambda-da olmaz: ad xarici dəyişənin adı ilə toqquşmamalıdır.
Nümunə:
int x = 10;
Runnable r = new Runnable() {
@Override
public void run() {
int x = 20; // OK: xarici dəyişəni kölgələyir
System.out.println(x); // 20
}
};
r.run();
Runnable l = () -> {
// int x = 30; // Kompilyasiya xətası: dəyişən artıq müəyyən edilib
System.out.println(x); // 10
};
l.run();
7. Lambda nə vaxt daha yaxşıdır, anonim sinif nə vaxt əvəzolunmazdır?
Lambda ifadələri — sizin seçiminiz, əgər:
- funksional interfeys üçün qısa bir funksiya reallaşdırmaq lazımdırsa;
- vəziyyəti saxlamaq tələb olunmursa;
- Object metodlarını yenidən təyin etmək tələb olunmursa;
- reallaşdırma “burada və indi” istifadə olunur və sadədir.
Anonim sinif — zəruridir, əgər:
- bir neçə metodlu interfeysi və ya abstrakt sinfi reallaşdırmaq lazımdırsa;
- sahələr və ya əlavə metodlar elan etmək lazımdırsa;
- toString, equals, hashCode kimi metodları yenidən təyin etmək tələb olunursa;
- super sinifin qorunan üzvlərinə çıxış lazımdırsa.
8. Təcrübə: nümunələr üzərində müqayisə
Tapşırıq 1: Siyahının Predicate vasitəsilə filtrdən keçirilməsi
Anonim sinif:
List<String> animals = Arrays.asList("pişik", "fil", "siçan", "pələng");
animals.removeIf(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.length() < 4;
}
});
System.out.println(animals); // [pişik, siçan, pələng]
Lambda ifadəsi:
List<String> animals = Arrays.asList("pişik", "fil", "siçan", "pələng");
animals.removeIf(s -> s.length() < 4);
System.out.println(animals); // [pişik, siçan, pələng]
Tapşırıq 2: this görünürlük sahəsinin müqayisəsi
public class Demo {
String name = "Demo";
void check() {
Runnable anon = new Runnable() {
String name = "Anon";
@Override
public void run() {
System.out.println(this.name); // "Anon"
}
};
Runnable lambda = () -> System.out.println(this.name); // "Demo"
anon.run();
lambda.run();
}
public static void main(String[] args) {
new Demo().check();
}
}
9. Anonim siniflər və lambda ifadələri ilə işləyərkən tipik səhvlər
Səhv №1: Lambda-nın bir neçə metodu reallaşdıracağını gözləmək. Lambda yalnız funksional interfeyslərlə (bir abstrakt metod) işləyir. Əgər metodlar daha çoxdursa — anonim sinifdən istifadə edin.
Səhv №2: this görünürlük sahəsi ilə bağlı çaşqınlıq. Lambda-da this — xarici sinifdir, anonim sinifdə isə — elə həmin anonim sinif. Buna görə də “düzgün olmayan” sahə və dəyərləri götürmək asandır.
Səhv №3: Lambda-da sahələr elan etməyə cəhd. Lambda-da öz sahələrinizi elan etmək olmaz — yalnız xarici kontekstdən dəyişənlərdən (final/“effektiv final”) istifadə edə bilərsiniz. Vəziyyət üçün anonim sinifdən istifadə edin.
Səhv №4: Dəyişənlərin kölgələnməsi. Anonim sinifdə xarici sahə ilə eyni adda lokal dəyişən elan etmək olar — bu, kölgələmədir. Lambda-da belə olmaz: kompilyator xəta verəcək.
Səhv №5: Lambda-da həddən artıq mürəkkəb məntiq. Əgər lambda-nın bədəni 3–5 sətri keçirsə, oxunaqlılıq əziyyət çəkir. Kodu ayrıca metoda çıxarmaq və ya (vəziyyət/ bir neçə metod lazımdırsa) anonim sinif tətbiq etmək daha yaxşıdır.
GO TO FULL VERSION