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
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.
GO TO FULL VERSION