CodeGym /Kurslar /JAVA 25 SELF /Lambda ifadələrinin üstünlükləri və çatışmazlıqları

Lambda ifadələrinin üstünlükləri və çatışmazlıqları

JAVA 25 SELF
Səviyyə , Dərs
Mövcuddur

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, Runnablevoid, 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.

Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION