CodeGym /Kurslar /JAVA 25 SELF /İrs alma və metodların yüklənməsi (overloading) ilə bağlı...

İrs alma və metodların yüklənməsi (overloading) ilə bağlı səhvlər

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

1. İrs alma zamanı səhvlər

İrs alma — obyekt yönümlü proqramlaşdırmanın (OOP) əsaslarından biridir, amma yeni başlayanların ən çox tələyə düşdüyü mövzulardan da biridir. Gəlin klassik səhvləri nəzərdən keçirək və onlardan necə qaçmağı öyrənək.

Baza sinfinin konstruktorunun çağırılmaması (super(...))

Alt sinif yaratdığınız zaman bunu unutmayın: baza sinfi konstruktor vasitəsilə müəyyən ilkinləşdirmə tələb edə bilər. Əgər baza sinfində susmaya görə (parametrsiz) konstruktor yoxdursa, onda varisin konstruktorunda mütləq super(...) ilə baza sinfinin uyğun konstruktorunu açıq şəkildə çağırmaq lazımdır.

Səhv nümunəsi:

class Animal {
    private String name;
    public Animal(String name) {
        this.name = name;
    }
}

class Dog extends Animal {
    // Xəta! Animal üçün susmaya görə konstruktor yoxdur
    public Dog() {
        // super(); // kompilyator super() çağırışını avtomatik əlavə edir, amma belə bir konstruktor mövcud deyil!
    }
}

Necə düzəltmək olar:

class Dog extends Animal {
    public Dog(String name) {
        super(name); // Hər şey qaydasındadır!
    }
}

Şərh:
Əgər baza sinfində yalnız parametrlərlə konstruktor varsa, kompilyator avtomatik parametrsiz konstruktor əlavə etməyəcək. Bu, kompilyasiya xətalarının geniş yayılmış səbəbidir.

final sinfindən irs almağa cəhd və ya final metodu yenidən yazmaq

Java-da sinfi və ya metodu final kimi elan etmək olar. Bu o deməkdir ki:

  • Sinifdən irs almaq olmaz.
  • Metodu varisdə yenidən yazmaq (override) olmaz.

Səhv nümunəsi:

final class Cat {}

// Kompilyasiya xətası!
class Tiger extends Cat { 
    // ...
}
class Animal {
    public final void sleep() {
        System.out.println("Zzz...");
    }
}

class Dog extends Animal {
    // Kompilyasiya xətası!
    @Override
    public void sleep() {
        System.out.println("Dog is sleeping...");
    }
}

Şərh:
Əgər “cannot inherit from final”, “cannot override final method” xətasını görürsünüzsə — modifikatorları yoxlayın!

Liskov əvəzləmə prinsipinin (Liskov Substitution Principle) pozulması

Qulağa çətin gələ bilər, amma praktikada bu belədir: alt sinfin obyekti, proqramın məntiqini pozmadan, baza sinfinin obyekti kimi davrana bilməlidir. Tez-tez edilən səhv — metodları elə yenidən yazmaqdır ki, yeni sinfin davranışı bazadan gözlənilənə uyğun gəlmir.

Nümunə:

class Bird {
    public void fly() {
        System.out.println("Mən uçuram!");
    }
}

class Penguin extends Bird {
    @Override
    public void fly() {
        throw new UnsupportedOperationException("Pinqvinlər uça bilmirlər!");
    }
}

Problem nədir?
Bird ilə işləyən kod hər bir quşun uça bildiyini gözləyir. Amma ona Penguin verilsə, proqram çökmə ehtimalı var.

Necə daha yaxşı:
Belə hallarda ierarxiyanı yenidən nəzərdən keçirmək və ya interfeyslər/kompozisiyadan istifadə etmək daha düzgündür.

2. Metodların yüklənməsi (overloading) ilə bağlı səhvlər

Overloading — eyni adda, fərqli parametrlərlə bir neçə metodun eyni sinifdə mövcud olmasıdır. Sadə görünür, amma burada da tələlər var.

Yenidən yazma (overriding) əvəzinə yükləmə (overloading) (imza səhvi)

Tez-tez yeni başlayanlar baza sinfinin metodunu yenidən yazmaq (override) istəyirlər, amma təsadüfən onun parametrlərini dəyişirlər. Nəticədə overloading alınır, overriding yox — və polimorfizm işləmir!

Səhv nümunəsi:

class Animal {
    public void makeSound() {
        System.out.println("Some sound");
    }
}

class Dog extends Animal {
    // Yenidən yazmaq istəyirdilər, amma yükləmə etdilər!
    public void makeSound(String extra) {
        System.out.println("Bark! " + extra);
    }
}

Problem:
dog.makeSound() çağırışı sizin yenisini deyil, valideyn metodunu çağıracaq.
dog.makeSound("loudly") yüklənmiş olanı çağıracaq, amma polimorfizm işləmir!

Yaxşı təcrübə:
@Override annotasiyasından istifadə edin — imzada səhv etsəniz, kompilyator dərhal xəbər verəcək.

@Override
public void makeSound() { /* ... */ }

Yükləmədə aydın olmayan davranış (avtomatik tip çevrilməsi, çağırışın ikimənalılığı)

Bəzən Java, parametrlər bir neçə yüklənmiş metodla uyğun gəlirsə, sizin gözləmədiyiniz metodu “seçə” bilər.

public class OverloadDemo {
    public void print(int x) {
        System.out.println("int: " + x);
    }
    public void print(double x) {
        System.out.println("double: " + x);
    }
}

OverloadDemo demo = new OverloadDemo();
demo.print(5);     // int: 5
demo.print(5.0);   // double: 5.0
demo.print(5L);    // long -> double: double: 5.0

Problem:
Əgər demo.print(5L) çağırılarsa, Java print(double x) metodunu seçəcək (çünki long, int-dən daha çox double-a çevrilməyə uyğundur).
Əgər Object, Integer, int tipli parametrlərlə metodlar varsa, null ilə çağırış kompilyasiya xətasına səbəb ola bilər: “reference to print is ambiguous”.

Yalnız qaytarılan tipə görə fərqlənən eyni adlı metodlardan istifadə (kompilyasiya xətası)

Java-da eyni adda və eyni parametr siyahısına malik, yalnız qaytarılan tipinə görə fərqlənən iki metodu elan etmək olmaz!

public class Demo {
    // Kompilyasiya xətası!
    public int foo() { return 1; }
    public String foo() { return "hello"; }
}

İzah:
Overloading üçün metodun imzası — ad + parametrlərdir. Qaytarılan tip nəzərə alınmır. Kompilyator hansı metodu çağırmaq istədiyinizi müəyyən edə bilməyəcək.

3. Ən yaxşı təcrübələr

İrs alma və yükləmə ilə bağlı tələlərə düşməmək üçün bu tövsiyələrə əməl edin:

Yenidən yazılan metodlar üçün həmişə @Override annotasiyasından istifadə edin

Bu, yalnız oxunaqlılığı artırmır, həm də imzadakı səhvlərdən qoruyur. Parametrləri və ya metodun adını təsadüfən dəyişsəniz, kompilyator dərhal xəbər verəcək.

@Override
public void makeSound() {
    System.out.println("Bark!");
}

Overloading və overriding-i dəqiq fərqləndirin

  • Overriding (yenidən yazma): valideyn metodunun davranışını dəyişirsiniz — imza tam eyni olmalıdır.
  • Overloading (yükləmə): eyni adla, fərqli parametrlərlə yeni metod əlavə edirsiniz.

Vizual cədvəl:

Yükləmə (overloading) Yenidən yazma (overriding)
Harada Eyni sinifdə/ierarxiyada Alt sinifdə
Metodun adı Eynidir Eynidir
Parametrlər Fərqlidir Eynidir
Qaytarılan dəyər Fərqli ola bilər Eyni olmalıdır/uyğun olmalıdır
Annotasiya Tələb olunmur @Override tövsiyə olunur

Overloading-dən həddindən artıq istifadə etməyin

Əgər metodun həddən artıq çox yüklənmiş variantı varsa, kod oxunmaz və dolaşıq olur. Çox variant olduqda, obyekt-parametrlərdən və ya Builder pattern-dən istifadə etmək daha yaxşıdır.

4. Nümunə: ev heyvanlarının uçotu sistemi

Tutaq ki, sadə bir ev heyvanları uçotu sistemi hazırlayırsınız. Sizdə baza sinfi Pet və ondan törəyən CatDog sinifləri var.

public class Pet {
    private String name;
    public Pet(String name) {
        this.name = name;
    }
    public void speak() {
        System.out.println(name + " anlaşılmaz bir səs çıxarır.");
    }
}

public class Cat extends Pet {
    public Cat(String name) {
        super(name);
    }
    @Override
    public void speak() {
        System.out.println(getName() + " deyir: Miyau!");
    }
    // Xəta: Pet daxilində getName() metodu yoxdur!
}

Tipik xəta:
Metodu yenidən yazmağa çalışarkən, baza sinfində olmayan bir metoda müraciət edirik. Daha yaxşısı, getter əlavə etməkdir:

public class Pet {
    private String name;
    public Pet(String name) { this.name = name; }
    public String getName() { return name; }
    public void speak() { System.out.println(name + " anlaşılmaz bir səs çıxarır."); }
}

İndi hər şey düzgün işləyir və polimorfizmdən istifadə edə bilərik:

Pet myPet = new Cat("Barsik");
myPet.speak(); // Barsik deyir: Miyau!

5. İrs alma və yükləmə ilə bağlı tipik səhvlər

Səhv №1: super(...) çağırışını unutdunuz.
Əgər baza sinfinin konstruktorunda vacib məntiq varsa, siz isə onu çağırmırsınızsa, proqram gözlənilməz davrana və ya ümumiyyətlə kompilyasiya olunmaya bilər.

Səhv №2: düzgün metodu yenidən yazmadınız.
Valideyn metodunun davranışını dəyişmək istəyirdiniz, amma əslində oxşar ad və ya fərqli parametrlərlə yeni metod əlavə etdiniz. Nəticə: köhnə metod əvvəlki kimi işləyir, yeni metodunuz isə heç kim tərəfindən çağırılmır.

Səhv №3: final metodu yenidən yazmağa cəhd etdiniz.
Java buna icazə verməyəcək, və bu, yaxşıdır! Amma kompilyasiya xətası görürsünüzsə — final axtarın.

Səhv №4: metodu həddindən artıq yüklədiniz.
Əgər calculate metodunun 10 variantı varsa və hansı çağırıldığını artıq özünüz də qarışdırırsınızsa — dayanmağın və refaktoring haqqında düşünməyin vaxtıdır.

Səhv №5: Liskov prinsipini pozdunuz.
Alt sinfiniz baza sinfinin davranışının mənasını dəyişirsə, bütün arxitektura “sürüşə” bilər. Məsələn, Shape sinfində getArea() metodu varsa, amma alt sinif BrokenShape -1 qaytarırsa, bu, qəribə xətalara gətirib çıxara bilər.

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