CodeGym /Kurslar /JAVA 25 SELF /İnterfeyslərdə default-metodlar

İnterfeyslərdə default-metodlar

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

1. Giriş

Bir zamanlar (Java 8-dən əvvəl) interfeys çox sərt idi: orada yalnız abstrakt metodlar (reallaşdırmasız) və konstantlar (public static final) elan etmək olurdu. Bu, bir böyük problem yaranana qədər rahat idi: kitabxanələrin inkişafı.

Təsəvvür edin situasiyanı

Siz populyar bir kitabxana hazırlamısınız, onda belə bir interfeys var:

public interface Movable {
    void move(int x, int y);
}

Dünyanın hər yerindən minlərlə proqramçı bu interfeysi reallaşdıran siniflər yazır. Bir neçə ildən sonra anlayırsınız ki, hamıya obyektin başlanğıc mövqeyinə qayıtmasını təmin edən reset() metodı çatışmır. Siz interfeysə əlavə edirsiniz:

public interface Movable {
    void move(int x, int y);
    void reset();
}

Və burada fəlakət başlayır: bu interfeysi istifadə edən bütün layihələr kompilyasiya olunmağı dayandırır! Axı indi onlar yeni metodu reallaşdırmağa borcludur, amma bundan heç kəs xəbərdar deyildi. Miqrasiya əzaba çevrilir.

Default-metodlar — həll yoludur!

Java 8 default-metodları təqdim etdi: artıq metodu birbaşa interfeysdə reallaşdırma ilə əlavə etmək olar! Bütün köhnə siniflər avtomatik olaraq standart reallaşdırma alır və onların kodu sınmır. İstəsəniz — metodu özünüzə uyğun şəkildə override edə bilərsiniz.

2. Default-metodların sintaksisi

Default-metod — bu, interfeys daxilində reallaşdırması olan və default açar sözü ilə işarələnən adi metoddur.

public interface Movable {
    void move(int x, int y);

    default void reset() {
        // Tipik reallaşdırma: koordinatların başlanğıcına qayıdırıq
        move(0, 0);
    }
}

İzah:

  • İnterfeysdə bütün metodlar susmaya görə public və abstract-dır, lakin default-metodlar — abstrakt deyil, bədənə malikdir.
  • default açar sözü həmişə metodun qaytarılan tipinin qarşısında yazılır.

Bu sinifdə necə görünür?

public class Robot implements Movable {
    private int x, y;

    @Override
    public void move(int x, int y) {
        this.x = x;
        this.y = y;
        System.out.println("Robot (" + x + ", " + y + ") nöqtəsinə hərəkət etdirildi");
    }

    // reset() metodunu reallaşdırmaq şərt deyil — default versiya işləyəcək!
}

İndi, əgər reset() metodunu Robot obyektində çağırsanız, Movable interfeysindəki reallaşdırma işləyəcək:

public class Main {
    public static void main(String[] args) {
        Movable robot = new Robot();
        robot.move(10, 20); // Robot (10, 20) nöqtəsinə hərəkət etdirildi
        robot.reset();      // Robot (0, 0) nöqtəsinə hərəkət etdirildi
    }
}

3. Standart kitabxanada default-metodlar

Default-metodlar sadəcə belə əlavə edilmədi, məqsəd Java-nın böyük standart interfeyslərini köhnə kodu sındırmadan inkişaf etdirmək idi.

Nümunə: List interfeysi (Java 8+)

Java 8-də List interfeysinə reallaşdırması olan metodlar əlavə edildi, məsələn, forEach, replaceAll, sort:

default void forEach(Consumer<Entity> action) {
    for (Entity e : this) {
        action.accept(e);
    }
}

Əgər öz siyahınızı reallaşdırırsınızsa və forEach-i override etməmisinizsə, o, yenə də işləyəcək — default-metod sayəsində.

Generik tiplər haqqında (Consumer<Entity>) daha ətraflı 26-cı səviyyədə öyrənəcəksiniz :P

4. Default-metodlar nə üçün lazımdır?

  • API-ni kodu sındırmadan inkişaf etdirmək: interfeysə yeni metodlar əlavə etmək olar və mövcud siniflərin hamısında onları reallaşdırmaq məcburi deyil.
  • Universal davranış şablonları: susmaya görə (default) davranışı elan etmək olar ki, siniflər ondan istifadə etsin və ya override etsin.
  • Təkrarlanmanın azaldılması: əgər davranışın çoxlu reallaşdırmalar üçün eyni olduğu hallarda — hər sinifə kodu köçürməyə ehtiyac yoxdur.

Analoqiya

Təsəvvür edin, sizdə mənzil kirayəsi müqaviləsi (interfeys) var. Orada əvvəllər belə yazılmışdı: “Kirayəçi su pulunu ödəməlidir”. Sonra əlavə edildi: “Kirayəçi elektrik pulunu ödəməlidir”. Əgər default-metodlar olmasaydı, bütün kirayəçilərlə bütün müqavilələri yenidən yazmalı olardınız! Default-metodlarla isə — sadəcə bənd əlavə edirsiniz, kiməsə lazım olarsa — öz aralarında başqa cür razılaşa bilərlər.

5. Default-metodların məhdudiyyətləri və xüsusiyyətləri

Object sinfinin metodlarını default-metodlarla override etmək olmaz

Siz interfeysdə Object sinfindəki equals, hashCode və ya toString ilə eyni siqnaturaya malik default-metod elan edə bilməzsiniz. Bu, çaşqınlığın qarşısını alır: axı Java-da hər bir obyekt artıq bu metodlara malikdir.

// Kompilyasiya xətası!
interface Broken {
    default boolean equals(Object obj) { return false; }
}

Default-metodların konfliktləri

Bəs əgər sinif iki interfeysi reallaşdırırsa və hər birində eyni siqnaturaya malik default-metod varsa? Java kompilyatoru səmimi deyəcək: “Özün qərar ver, nə edəcəyimi bilmirəm!”

interface A {
    default void hello() { System.out.println("Hello from A"); }
}

interface B {
    default void hello() { System.out.println("Hello from B"); }
}

class C implements A, B {
    // Mütləq konflikti həll edin:
    @Override
    public void hello() {
        // Hansı metodun çağırılacağını seçmək və ya öz reallaşdırmanızı yazmaq olar
        A.super.hello(); // və ya B.super.hello();
    }
}

Əgər hello() metodunu C sinfində reallaşdırmasanız, kompilyasiya xətası olacaq.

Default-metodlar interfeysin digər metodlarını çağıra bilər

Default-metod interfeysin digər metodlarını, hətta abstrakt olanları belə çağıra bilər. Əsas odur ki, sinifdə onların reallaşdırması olsun.

interface Printer {
    void print(String text);

    default void printTwice(String text) {
        print(text);
        print(text);
    }
}

6. Nümunə: tətbiqi default-metodla inkişaf etdiririk

Movable interfeysində default-metodlardan istifadə nümunəsinə baxaq:

public interface Movable {
    void move(int x, int y);

    default void reset() {
        move(0, 0);
    }
}

Və bu interfeysi reallaşdıran Robot sinfi var:

public class Robot implements Movable {
    private int x = 5;
    private int y = 7;

    @Override
    public void move(int x, int y) {
        this.x = x;
        this.y = y;
        System.out.println("Robot (" + x + ", " + y + ") nöqtəsinə hərəkət etdirildi");
    }

    // reset() reallaşdırmırıq — default-metoddan istifadə edirik!
}

İndi hər iki metodu çağıraq:

public class Main {
    public static void main(String[] args) {
        Movable robot = new Robot();
        robot.move(10, 20); // Robot (10, 20) nöqtəsinə hərəkət etdirildi
        robot.reset();      // Robot (0, 0) nöqtəsinə hərəkət etdirildi
    }
}

Əgər istəsək ki, Robot xüsusi qaydada sıfırlansın — sadəcə sinifdə reset() metodunu override edirik:

@Override
public void reset() {
    System.out.println("Robot söndürülür və bazaya qayıdır!");
    move(0, 0);
}

7. Default-metodlar və interfeyslərin çoxsaylı reallaşdırılması

Default-metodlar xüsusilə sinif bir neçə interfeysi reallaşdıranda faydalıdır. Amma bir nüans var: hər iki interfeys eyni siqnaturaya malik default-metoda sahibdirsə, kompilyator konfliktin açıq şəkildə həllini tələb edəcək.

Konflikt nümunəsi

interface A {
    default void show() { System.out.println("A"); }
}
interface B {
    default void show() { System.out.println("B"); }
}
class C implements A, B {
    @Override
    public void show() {
        // Hansı default-metoddan istifadə olunacağını açıq şəkildə seçirik
        A.super.show(); // və ya B.super.show();
    }
}

8. Sxem: default-metodun çağırışı necə işləyir


+-------------------+
|   Movable         |
|-------------------|
| +move(int, int)   | <- abstrakt metod
| +reset()          | <- default-metod
+-------------------+
         ^
         |
+-------------------+
|   Robot           |
|-------------------|
| +move(int, int)   | <- reallaşdırır
|                   | (reset() reallaşdırmır)
+-------------------+
         |
     reset() çağırışı
         |
   Reallaşdırma istifadə olunur
   Movable interfeysindən olan
Default-metodun çağırışı: interfeysdən susmaya görə reallaşdırma

9. Default-metodlarla işləyərkən tipik səhvlər

Səhv №1: reallaşdırmasız default-metod yazmağa cəhd.
Default-metodun mütləq bədəni olmalıdır! Əgər default void foo(); yazsanız, kompilyator dərhal deyəcək: “Sən nə, qıvrım mötərizələri unutdun?”

Səhv №2: müxtəlif interfeyslərdən gələn default-metodların konflikti.
Əgər sinif eyni default-metoda sahib iki interfeysi reallaşdırırsa, konflikti açıq şəkildə həll etməlisiniz — əks halda kompilyator koda kompilyasiya icazəsi verməyəcək.

Səhv №3: Object sinfinin metodu ilə eyni siqnaturalı default-metod elan etmək cəhdi.
İnterfeysdə equals, hashCode və ya toString üçün default-metod etmək olmaz — bu adlarla yalnız abstrakt metodlar ola bilər.

Səhv №4: default-metodların “sehr” olduğunu unutmaq.
Default-metodlar “sehr” deyil, sadəcə rahat vasitədir. İnterfeys hələ də bir müqavilədir. Əgər susmaya görə davranış uyğun deyilsə — default-metodu sinifdə həmişə override edin.

1
Tapşırıq
JAVA 25 SELF, səviyyə, dərs
Bağlanıb
Ağıllı cihazın idarəetmə pultu ⚙️
Ağıllı cihazın idarəetmə pultu ⚙️
1
Tapşırıq
JAVA 25 SELF, səviyyə, dərs
Bağlanıb
Zooparkdakı müxtəlif heyvanların davranışı 🐕
Zooparkdakı müxtəlif heyvanların davranışı 🐕
1
Tapşırıq
JAVA 25 SELF, səviyyə, dərs
Bağlanıb
İkiqat bildiriş sistemi 🔔🔔
İkiqat bildiriş sistemi 🔔🔔
1
Tapşırıq
JAVA 25 SELF, səviyyə, dərs
Bağlanıb
Fərqli sistemlərdən modulların inteqrasiyası 🧩
Fərqli sistemlərdən modulların inteqrasiyası 🧩
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION