1. Daxili sinif (inner class) nədir?
Java-da sinif təkcə ən üst səviyyədə (faylda) deyil, həm də başqa bir sinifin daxilində elan edilə bilər. Belə sinif vələd (nested class) adlanır. Əgər belə sinif static modifikatoru olmadan elan edilirsə, o, daxili sinif (yəni non-static inner class, qısaca inner class) adlanır.
Daxili sinif — başqa bir sinifin daxilində elan olunan və həmin xarici sinifin nümunəsi ilə əlaqəli olan sinifdir. O, xarici sinifin bütün sahə və metodlarına, hətta onlar private olduqda belə, müraciət edə bilər. Bu, sanki obyektin hər şeyə icazəsi olan «gizli köməkçisi» var kimi bir şeydir!
Bu, təxminən otaqları olan bir ev kimidir. Ev — xarici sinif, otaq — daxili sinifdir. Otaq evsiz mövcud ola bilməz, amma evin resurslarına çıxışı var: işıq, isitmə, mebel. Ev yox olsa, otaq da yox olar. Java-da daxili siniflər məhz belə işləyir!
Daxili siniflər nə üçün lazımdır?
- Məntiqi əlaqə: Bir sinif yalnız digər siniflə işləmək üçün lazımdırsa və ondan kənarda mənası yoxdursa.
- İnkapulyasiya: Paket ad məkanını «çirkləndirmədən» reallaşdırma detalları gizlədilir.
- Private üzvlərə çıxış: Daxili sinif xarici sinifin private sahə və metodlarına müraciət edə bilər.
- Kompaktlıq: Paket səviyyəsində «artıq» siniflərin sayını azaldır.
Daxili sinfin elan sintaksisi
Daxili sinfi elan etmək çox asandır: o, başqa bir sinifin gövdəsində static modifikatoru olmadan elan edilir.
class Outer {
// xarici sinifin sahələri və metodları
class Inner {
// daxili sinifin sahələri və metodları
void printHello() {
System.out.println("Hello from Inner!");
}
}
}
Burada Inner, Outer-a nisbətən daxili sinifdir.
Şəkil: Vizualizasiya
Outer
│
├─ sahələr/metodlar
│
└─ Inner (daxili sinif)
└─ öz sahələri/metodları
2. Daxili sinifin nümunəsini necə yaratmalı
Məhz burada yeni başlayanların ən çox düşdüyü tələlərdən biri gizlənir! Daxili sinifin nümunəsi həmişə konkret bir xarici sinif obyektinə bağlıdır.
Nümunə:
Outer outer = new Outer(); // xarici sinifin obyektini yaradırıq
Outer.Inner inner = outer.new Inner(); // daxili sinifi məhz xarici obyekt vasitəsilə yaradırıq!
inner.printHello(); // Hello from Inner!
Təkcə new Inner() yazmaq kompilasiya xətasına gətirib çıxaracaq, çünki Java bu daxili sinifi hansı xarici sinif obyektinə bağlamağı bilmir.
Niyə belədir?
Daxili sinif xarici sinifin sahə və metodlarına müraciət edə bilər. Buna görə də o, hansı xarici sinif obyektilə işlədiyini «bilməlidir».
3. Daxili sinifin istifadəsinə nümunə
Gəlin daxili sinfin həqiqətən faydalı olduğu bir nümunəyə baxaq.
Nümunə 1: «Çanta və əşyalar» modeli
Tutaq ki, bizdə Backpack sinifi var və o, əşyaları (Item) saxlaya bilir. Amma istəyirik ki, Item sinifi yalnız Backpack-in daxilində əlçatan olsun, çünki çantadan kənarda əşyalar bizi maraqlandırmır.
public class Backpack {
private String owner;
public Backpack(String owner) {
this.owner = owner;
}
// Daxili sinif — əşya yalnız çantada mövcud ola bilər!
class Item {
private String name;
public Item(String name) {
this.name = name;
}
public void printInfo() {
// Sehr! Xarici sinifin private sahəsini görürük
System.out.println(owner + " əşyaya malikdir: " + name);
}
}
}
İstifadə:
Backpack bp = new Backpack("Vasya");
Backpack.Item item = bp.new Item("Java dərsliyi");
item.printInfo(); // Vasya əşyaya malikdir: Java dərsliyi
Diqqət yetirin: Item owner sahəsinə, o private olsa belə, müraciət edə bilir!
Nümunə 2: Öz kolleksiyan üçün iterator
Java-da kolleksiyalar tez-tez daxili sinif-iterator reallaşdırır. Gəlin daxili sinif-iteratoru olan sadə bir kolleksiya yazaq.
public class IntList {
private int[] data = new int[10];
private int size = 0;
public void add(int value) {
data[size++] = value;
}
// Daxili sinif — iterator!
class Iterator {
private int index = 0;
public boolean hasNext() {
return index < size;
}
public int next() {
return data[index++];
}
}
}
İstifadə:
IntList list = new IntList();
list.add(10);
list.add(20);
IntList.Iterator it = list.new Iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
4. Daxili siniflərin xüsusiyyətləri
Xarici sinifin üzvlərinə çıxış
Daxili sinif xarici sinifin istənilən (hətta private) sahə və metodlarına müraciət edə bilər. Bu rahatdır, amma ehtiyat tələb edir: əgər xarici sinif dəyişərsə, daxili sinif qəfil «sına bilər».
class Outer {
private int secret = 42;
class Inner {
void showSecret() {
System.out.println("Sirr: " + secret);
}
}
}
Daxili sinif statik üzvlər tuta bilməz
Daxili (non-static inner) sinif statik sahə və ya metodları tuta bilməz, yalnız statik final-konstantalar istisnadır (məsələn, public static final int MY_CONST = 123;). Bu onunla bağlıdır ki, daxili sinif həmişə xarici sinifin nümunəsinə «bağlıdır».
Əgər sizə statik vələd sinif lazımdırsa — static modifikatorundan istifadə edin (bu barədə növbəti mühazirədə).
Daxili sinfin görünürlüğü
Daxili sinif private, protected, public ola bilər və ya paket səviyyəsində görünürlüyə (package-private) malik ola bilər.
public class Outer {
private class Inner { /* ... */ }
}
5. Faydalı nüanslar
Daxili sinifləri nə zaman istifadə etməli
Daxili siniflər gözəllik üçün yox, kodun məntiqi təşkili üçün istifadə olunur. Onlardan aşağıdakı hallarda istifadə edin:
- Sinif yalnız bir yerdə lazımdırsa (məsələn, köməkçi iterator, emalçı, mürəkkəb quruluşun hissəsi).
- Sinif xarici siniflə sıx bağlıdırsa və ondan kənarda mənası yoxdursa.
- Reallaşdırma detallarını paketdəki digər siniflərdən gizlətmək istəyirsinizsə.
Daxili sinifləri sadəcə dəb xatirinə istifadə etməyin! Bəzən sinifi üst səviyyəyə çıxarmaq daha yaxşıdır, əgər o, başqa yerlərdə də faydalı ola bilərsə.
Daxili sinif və this
Daxili sinfin içində xarici sinifin sahə və metodlarına OuterClassName.this vasitəsilə müraciət etmək mümkündür.
class Car {
private String model = "Tesla";
class Engine {
void printModel() {
// Aydın şəkildə xarici obyektə müraciət edirik
System.out.println("Model: " + Car.this.model);
}
}
}
Bu sintaksis xüsusilə faydalıdır, əgər daxili və xarici siniflərin eyni adlı sahələri varsa.
Daxili sinifləri istifadə ETMƏMƏK lazım olan hallar
İstifadə etməyin, əgər:
- Daxili sinif xarici sinifin sahə/metodlarına müraciət etmir
- Sinif proqramın digər yerlərində faydalı ola bilər
- Xarici sinif həddən artıq böyük və mürəkkəb olur
Belə hallarda daha yaxşıdır:
- Sinifi statik vələd (static class) etmək
- Sinifi ayrıca fayla çıxarmaq
- Bütün bir sinif əvəzinə adi metodlardan istifadə etmək
6. Daxili siniflərlə işləyərkən tipik səhvlər
Səhv № 1: Xarici sinifin obyektı olmadan daxili sinif yaratmağa cəhd.
Əgər new Inner() yazsanız, kompilyator belə bir xəta verəcək: "No enclosing instance of type Outer is accessible". Həmişə daxili sinifi xarici sinifin obyekti vasitəsilə yaradın: outer.new Inner().
Səhv № 2: Daxili sinifdə statik sahə və ya metodlar elan etmək cəhdi.
Daxili sinif statik üzvlər tuta bilməz (konstantalar istisna olmaqla). Lazımdırsa — statik vələd sinifdən istifadə edin, yəni sinifi static modifikatoru ilə elan edin.
Səhv № 3: Daxili siniflərin həddən artıq istifadəsi.
Əgər daxili sinif xarici sinifin sahə/metodlarından istifadə etmirsə, çox güman ki, o, static olmalıdır və ya ümumiyyətlə kənara çıxarılmalıdır. Daxili sinifləri «ehtiyat üçün» yaratmayın.
Səhv № 4: Xarici sinifin sahələrinə müraciətdə çaşqınlıq.
Əgər daxili və xarici siniflərin eyni adlı sahələri varsa, xarici sinifin sahəsinə aydın müraciət üçün OuterClassName.this.field istifadə edin.
GO TO FULL VERSION