CodeGym /Kurslar /JAVA 25 SELF /Anonim siniflər: lambda ilə fərqlər, nümunələr

Anonim siniflər: lambda ilə fərqlər, nümunələr

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

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.

1
Tapşırıq
JAVA 25 SELF, səviyyə, dərs
Bağlanıb
Rəqəmsal zooparkda senzura 🦒
Rəqəmsal zooparkda senzura 🦒
1
Tapşırıq
JAVA 25 SELF, səviyyə, dərs
Bağlanıb
Daxili dünyanın səsləri 🎭
Daxili dünyanın səsləri 🎭
1
Sorğu/viktorina
, səviyyə, dərs
Əlçatan deyil
Lambda ifadələri
Lambda ifadələri
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION