CodeGym /Kurslar /JAVA 25 SELF /Lambda ifadələri: sintaksis, görünürlük sahələri

Lambda ifadələri: sintaksis, görünürlük sahələri

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

1. Giriş

Düzünü deyək: bir-iki sətirlik kod üçün anonim siniflər yazmaq — sanki çörəkxanadan mağazaya bir bulka aparmaq üçün nəhəng yük maşını kirayələmək kimidir.

Məsələn, sətirləri uzunluğa görə çeşidləmək istəsəniz, Java 8-dən əvvəl belə yazmalı idiniz:

List<String> list = Arrays.asList("alma", "banan", "kivi");
Collections.sort(list, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.length() - b.length();
    }
});

Görünən odur ki, vəzifə sadədir, amma kod — yarım ekran. Xüsusən bu cür əməliyyatlar çox olanda qıcıqlandırır: kod “səs-küylü” olur, vəzifənin mahiyyəti itib gedir. Proqramçılar əziyyət çəkdilər və sonra lambda ifadələrini icad etdilər. Biz artıq bir az öyrənmişik, indi isə biliklərimizi təkrar edib dərinləşdirəcəyik.

Lambda ifadəsi nədir

Lambda ifadəsi — funksional interfeysin (yəni cəmi bir abstrakt metodu olan interfeysin; məsələn, Comparator, Runnable, Consumer və s.) reallaşdırılmasının yığcam yazılış formasıdır.

Daha sadə desək, lambda ifadəsi ayrıca sinif və ya metod elan etmədən, bir funksiyanı elə lazım olduğu yerdəcə “sürətlə” yazmağa imkan verir.

Ümumi sintaksis:

(parametrlər) -> { gövdə }

Nümunələr:

  • (a, b) -> a + b — iki ədədin toplanması funksiyası
  • x -> x * x — ədədin kvadratını alma funksiyası
  • () -> System.out.println("Hello!") — parametrlərsiz funksiya

Funksional interfeyslərlə əlaqə:
Lambda ifadəsini hər zaman funksional interfeys tipli dəyişənə mənimsətmək və ya həmin interfeysi gözləyən metoda arqument kimi ötürmək olar.

2. Lambda ifadələrinin sintaksisi

Parametrsiz

Runnable r = () -> System.out.println("Salam, dünya!");
r.run(); // Çap edəcək: Salam, dünya!

Bir parametr
Əgər parametr bir dənədirsə, mötərizəni atmaq olar:

Consumer<String> print = s -> System.out.println(s);
print.accept("Java əladır!");

Bir neçə parametr
Mötərizələr mütləqdir:

Comparator<String> cmp = (a, b) -> a.length() - b.length();

Bir ifadədən ibarət gövdə
Əgər gövdə bir ifadədən ibarətdirsə, süslü mötərizələr və return lazım deyil:

Function<Integer, Integer> square = x -> x * x;
System.out.println(square.apply(5)); // 25

Blokdan ibarət gövdə
Bir neçə təlimat lazımdırsa, süslü mötərizələrdən və (qaytarılan nəticə varsa) return-dən istifadə edin:

Function<Integer, Integer> abs = x -> {
    if (x < 0) {
        return -x;
    }
    return x;
};
System.out.println(abs.apply(-3)); // 3

Parametrlərin tipləri
Əksər hallarda parametrlərin tipini göstərməyə ehtiyac yoxdur — kompilyator kontekstdən özü anlayacaq. Amma istəsəniz, açıq şəkildə yaza bilərsiniz:

Comparator<String> cmp = (String a, String b) -> a.length() - b.length();

Qaytarma dəyəri olmayan lambda
Əgər interfeys void qaytarırsa, sadəcə təlimatları yazın:

list.forEach(s -> System.out.println("Element: " + s));

Cədvəl: Lambda ifadələrinin sintaksis variantları

Nə edirik Nümunə Şərh
Parametrsiz
() -> System.out.println("Hi")
Məsələn, Runnable üçün
Bir parametr
x -> x * x
Mötərizəsiz mümkündür
Bir neçə parametr
(a, b) -> a + b
Mütləq mötərizə
Tək ifadə
x -> x + 1
return və süslü mötərizələrsiz
Kod bloku
x -> { int y = x + 1; return y * 2; }
Nəticə varsa, return ilə

3. Tətbiq: lambda ifadələrini harada və necə istifadə etməli

Lambda ifadələri ən çox “davranışı” — funksiyanı — arqument kimi ötürmək lazım olduqda istifadə edilir. Bu, kolleksiyalar, axınlar (Stream API), hadisələr və daha çox şey üçün əsl inqilab oldu.

Siyahının çeşidlənməsi

Java 8-dən əvvəl:

list.sort(new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return a.length() - b.length();
    }
});

Lambda ilə:

list.sort((a, b) -> a.length() - b.length());

Axının (Thread) yaradılması

Thread t = new Thread(() -> System.out.println("Axın işə düşdü!"));
t.start();

Kolleksiyaların emalı

list.forEach(s -> System.out.println(s.toUpperCase()));

Siyahının filtrasiyası

List<String> longWords = list.stream()
    .filter(s -> s.length() > 5)
    .collect(Collectors.toList());

Nümunə: Tədris tətbiqimizi inkişaf etdiririk

Tutaq ki, istifadəçi siyahımız var:

List<String> users = Arrays.asList("Alice", "Bob", "Charlie");

Adı 4 simvoldan uzun olan bütün istifadəçiləri çıxarmaq:

users.stream()
    .filter(name -> name.length() > 4)
    .forEach(name -> System.out.println("İstifadəçi: " + name));

4. Lambda ifadələrində dəyişənlərin görünürlük sahəsi

Lambda ifadələri əhatə edən metoddan dəyişənlərdən istifadə edə bilər, amma bəzi nüanslar var!

Dəyişənlər və lambdalar

Java-da lambda yalnız ilkinləşdirmədən sonra daha dəyişdirilməyən dəyişənləri “tuta” bilər. Əgər dəyişən final ilə elan olunubsa — hər şey aydındır. Amma söz final yazılmasa belə, kompilyator özü yoxlayır: dəyər dəyişir, ya yox. Dəyər dəyişmirsə, onu “guya final” hesab edir və ləngimədən lambda daxilində istifadə etməyə icazə verir.

Nümunə:

int minLength = 4; // dəyər heç yerdə dəyişmir
users.forEach(name -> {
    if (name.length() > minLength) {
        System.out.println(name);
    }
});

Burada hər şey işləyir, çünki minLength eyni ədəd olaraq qalır.

Amma lambdada istifadə etdikdən sonra minLength-ə yenidən dəyər verməyə çalışsanız, kompilyasiya xətası alacaqsınız:

int minLength = 4;
users.forEach(name -> {
    if (name.length() > minLength) {
        System.out.println(name);
    }
});
minLength = 10; // Xəta! Lambda artıq dəyəri "sabitlədi"

Mahiyyətcə qayda çox sadədir: lambdada istifadə olunan dəyişən dəyişməz olmalıdır.

Anonim siniflərdən fərqi

Anonim siniflərdə və lambda ifadələrində xarici metoddan gələn dəyişənlər eyni cür işləyir: yalnız final/effektiv final.

AMMA!
Lambda ifadəsində this xarici obyektə (sinifin cari nümunəsinə), anonim sinifdə isə anonim sinifin öz nümunəsinə istinad edir. Bu, lambdanın içində cari sinfin sahə və metodlarına müraciət edirsinizsə, mühümdür.

Nümunə:

public class Example {
    String name = "Xarici sinif";

    void demo() {
        Runnable r1 = new Runnable() {
            String name = "Anonim sinif";
            @Override
            public void run() {
                System.out.println(this.name); // "Anonim sinif"
            }
        };

        Runnable r2 = () -> System.out.println(this.name); // "Xarici sinif"

        r1.run();
        r2.run();
    }
}

5. Lambda ifadələri ilə işləyərkən tipik səhvlər

Xəta №1: final/effektiv final olmayan dəyişənin istifadəsi. Lambdada istifadə etdikdən sonra dəyişəni dəyişməyə qərar versəniz, kompilyator dərhal sizi saxlayacaq. Bu, təhlükəsizlik üçün edilir: əks halda hansı dəyərdən istifadə olunacağı aydın olmazdı.

Xəta №2: this ilə qarışıqlıq. Lambda ifadəsində this — xarici sinifdir, anonim sinifdə isə — anonim sinifin özüdür. Əgər lambdadan xarici sinfin metodunu çağırmağa çalışırsınızsa, hər şey işləyəcək, anonim sinifdən isə — yox (əgər xarici sinfin kontekstinə güvənirdinizsə).

Xəta №3: Kontekstsiz lambda. Lambda ifadəsini təkbaşına istifadə etmək olmaz — onu ya funksional interfeys tipli dəyişənə mənimsətməli, ya da həmin interfeysi gözləyən yerə ötürməlisiniz. Sadəcə x -> x + 1 yazmaq kontekstdən kənar olduqda xəta verəcək.

Xəta №4: Həddən artıq mürəkkəb lambda. Lambda ifadəsi 3–5 sətirdən uzun olursa, oxumaq çətinləşir. Belə hallarda məntiqi ayrıca metoda çıxarmaq daha yaxşıdır.

1
Tapşırıq
JAVA 25 SELF, səviyyə, dərs
Bağlanıb
Adların çeşidləmə emalatxanası 📜
Adların çeşidləmə emalatxanası 📜
1
Tapşırıq
JAVA 25 SELF, səviyyə, dərs
Bağlanıb
Mistik ədədlər klassifikatoru 🔮
Mistik ədədlər klassifikatoru 🔮
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION