1. Lambda ifadələrinin üstünlükləri
Lambda ifadələri təkcə sintaktik şəkər deyil, Java-da funksional üsluba doğru addımdır. Aşağıda — onların real üstünlükləri və müasir kodda niyə bu qədər rahat olduqları.
Qısalıq və ifadəlilik
Lambda-lardan əvvəl sadə “lokal” kod çoxlu şablon səs-küyü olan anonim sinfə çevrilirdi. Məsələn, sətirləri uzunluğa görə çeşidləmə:
Java 8-dən əvvəl (anonim sinif):
list.sort(new Comparator<String>() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
});
Lambda ifadəsi ilə:
list.sort((a, b) -> a.length() - b.length());
Kod daha qısadır və demək olar ki, təbii dil kimi oxunur: “uzunluqların fərqinə görə sırala”.
Oxunaqlılıq və məğzə fokus
Lambda-lar “xidmət səsi-küyünü” — sinif adlarını, artıq fiqurlu mötərizələri, returnləri aradan qaldırır. Nəticədə kodu oxumaq və dəstəkləmək daha asan olur:
names.forEach(name -> System.out.println(name));
Hər şey aydındır: hər ad üçün — onu çap etmək. Burada forEach kimi kolleksiya metodlarını bilmək əlverişlidir.
Davranışın parametr kimi ötürülməsi
Axır ki, “davranış parçasını” metodun parametri kimi ötürmək rahat oldu. Bu, xüsusən kolleksiyalarda, Stream API-də və hadisələrdə hiss olunur:
Nümunə: ədədlər siyahısının filtrlanması
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.removeIf(n -> n % 2 == 0); // Cüt ədədləri silirik
Kolleksiyalar və Stream API ilə əla inteqrasiya
List<String> words = Arrays.asList("Java", "Python", "C++");
List<String> upper = words.stream()
.map(s -> s.toUpperCase())
.collect(Collectors.toList());
Dəyişənlərin tutulması (closures)
Lambda-lar xarici kontekstdən dəyişənləri “tuta” bilər (əgər onlar effektiv olaraq finaldirsə). Bu, ətraf mühiti yadında saxlayan funksiyaları yerində yaratmağa imkan verir:
int minLength = 3;
list.removeIf(s -> s.length() < minLength);
Dəyişən minLength çöldə elan edilib, lakin lambda daxilində əlçatandır.
Hadisələr və callback-lər üçün təbii yazılış
button.addActionListener(e -> System.out.println("Düymə basıldı!"));
Artıq tək bir sətir üçün ayrıca sinif və ya anonim sinif yaratmağa ehtiyac yoxdur.
Testləşdirmənin sadələşdirilməsi
Siniflər yaratmadan tez bir zamanda “stub”lar qoşmaq olar:
doSomething(() -> System.out.println("Test işləyicisi"));
2. Lambda ifadələrinin çatışmazlıqları və məhdudiyyətləri
Hər alətdə olduğu kimi, burada da tələlər var.
Sazlama (debug) çətinlikləri
Lambda-lar anonim funksiyalardır; xəta baş verdikdə çağırış steki bəzən aydın olmur. Dayanma nöqtələri (breakpoint-lər) işləyir, amma uzun/iç-içə lambda-larda problemin dəqiq harada olduğunu anlamaq çətin ola bilər.
list.stream()
.filter(s -> s.length() > 3)
.map(s -> s.toUpperCase())
.forEach(System.out::println);
Bəzən zənciri ara dəyişənlərə “sökmək” kömək edir.
Reallaşdırılan interfeysin qeyri-aşkarlığı
Müxtəlif funksional interfeysləri qəbul edən overload-larda kompilyator lambda-nın konkret olaraq hansı interfeysi reallaşdırdığını anlaya bilməyə bilər (məsələn, Runnable — void, yaxud Callable — qaytarılan dəyər String).
void doSomething(Runnable r) { /* ... */ }
void doSomething(Callable<String> c) { /* ... */ }
// doSomething(() -> "Hello"); // Qeyri-müəyyənlik!
Mürəkkəb məntiq üçün uyğun deyil
Əgər lambda-nın gövdəsi 3–5 sətrə və daha çoxa qədər böyüyürsə (çoxlu şərtlər/döngələr), kod oxunaqlılığını itirir — məntiqi adlı metoda çıxarmaq daha yaxşıdır.
Pis nümunə:
list.removeIf(s -> s.length() > 3 && s.contains("Java") && s.startsWith("A") && ...);
Daha yaxşı:
list.removeIf(this::isComplexCondition);
private boolean isComplexCondition(String s) {
return s.length() > 3 && s.contains("Java") && s.startsWith("A") && ...;
}
Serializasiya məhdudiyyətləri
Lambda-lar hər zaman serializə oluna bilmir. Əgər məntiqi JVM-lər arasında ötürmək lazımdırsa (paylanmış sistemlər), anonim və ya adlı siniflərdən, yaxud açıq şəkildə Serializable-ı dəstəkləyən interfeyslərdən istifadə etmək daha etibarlıdır.
Görünüş sahəsi (scope) məhdudiyyətləri
Lambda daxilində, əgər xarici metodun dəyişənləri final və ya “effektiv olaraq” final deyilsə, onları dəyişmək olmaz.
int count = 0;
list.forEach(s -> count++); // Kompilyator buna icazə verməyəcək!
Təkrar istifadə üçün uyğun deyil
Lambda-lar “birdəfəlik” funksiyalardır. Məntiqi bir neçə yerdə istifadə etmək lazımdırsa — onu ayrıca, mənalı adına malik metod və ya sinifə çıxarın.
İç-içə lambda ifadələri ilə çətinliklər
Dərin iç-içəlik (xüsusən axınlarda/hadisə işləyicilərində) kodu tez bir zamanda “spagetti”yə çevirir. Yaxşısı budur, iç-içəliyi azaltmaq və ya addımlara bölmək.
Lambda ifadələrini nə vaxt istifadə etməli
- Qısa, sadə əməliyyatlar: filtrasiya, çeşidləmə, kolleksiyaların çevrilməsi, hadisələrin emalı.
- Lambda 3–5 sətirdən uzundursa — onu ayrıca metoda çıxarın.
- Mürəkkəb biznes məntiqi üçün lambda istifadə etməyin — məntiqə ad və şərhlər verin.
- İç-içə lambda-larla həddindən artıq məşğul olmayın.
- Təkrarlanan lambda-nı metoda (və ya statik metoda) çıxarın və this::method və ya ClassName::method şəklində istinaddan istifadə edin.
- Lambda daxilində parametrlərə məna kəsb edən adlar verin — bu, oxunaqlılığı artırır.
3. Praktik tövsiyələr
Mürəkkəb zəncirləri mərhələlərə bölün
Bir uzun zəncir əvəzinə — ara dəyişənlər:
Stream<String> filtered = list.stream().filter(s -> s.length() > 3);
Stream<String> upper = filtered.map(String::toUpperCase);
upper.forEach(System.out::println);
Mürəkkəb şərtlər üçün adlı metodlardan istifadə edin
Uzun lambda əvəzinə:
list.removeIf(s -> s.length() > 3 && s.contains("Java"));
Daha yaxşı:
list.removeIf(this::isJavaString);
private boolean isJavaString(String s) {
return s.length() > 3 && s.contains("Java");
}
Şərh verməkdən çəkinməyin
Əgər lambda aydın deyilsə — ondan əvvəl şərh əlavə edin:
// Boşluqla başlayan bütün sətrləri silirik
list.removeIf(s -> s.startsWith(" "));
4. Lambda ifadələri ilə işləyərkən tipik səhvlər
Səhv №1: Həddindən artıq mürəkkəb lambda. Yeni başlayanlar bütün biznes məntiqini bir lambda-ya sığışdırmağa çalışırlar. Nəticədə 10 sətirlik, oxunması və dəstəklənməsi çətin “canavarlar” alınır. Kodu metodlara ayırmaqdan çəkinməyin!
Səhv №2: Görünüş sahəsinin qeyri-aşkarlığı. Lambda içində xarici metodun dəyişənlərini dəyişməyə çalışırlar — kompilyator etiraz edir. Unutmayın: dəyişənlər final və ya “effektiv olaraq” final olmalıdır.
Səhv №3: Metodların overload edilməsi. Əgər müxtəlif funksional interfeysləri qəbul edən iki overload varsa, kompilyator hansını çağırmaq istədiyinizi anlaya bilməyə bilər. Belə hallarda tipi açıq şəkildə göstərin:
doSomething((Runnable) () -> System.out.println("Hello"));
Səhv №4: İç-içə lambda-lardan sui-istifadə. İç-içə lambda-lar kodu oxunmaz “spagetti”yə çevirir. Dayanın, kodun bir hissəsini ayrıca metoda çıxarın.
Səhv №5: Tam funksional obyekt lazım olan yerdə lambda-dan istifadə. Bir neçə metodu yenidən müəyyənləşdirmək, sahələr (field-lər) əlavə etmək və ya qeyri-standart davranış lazım olduqda — lambda deyil, anonim və ya adlı sinif istifadə edin.
GO TO FULL VERSION