Calling Constructors
Java`da parent constructor həmişə child constructordan əvvəl icra olunur:
class Continent { public Continent() { System.out.println("Continent"); } } class Country extends Continent { public Country() { System.out.println("Country"); } } class City extends Country { public static void main(String[] args) { new City(); } }
Output:
Continent
Country
Compiler əvvəlcə Continent
və Country
constructorlarının birinci sətrinə super()
əlavə edir. Sonra City
classına default no-argument constructor əlavə edir və birinci sətrinə də super()
. Bu səbəbdən də parent constructorlar ilk öncə icra olunur.
Calling Inherited Class Members
Child classlar parent classın istənilən public
və protected
üzvlərini (members) istifadə edə bilər. Əgər parent və child class eyni paketdə olarsa, o zaman parent classın default
üzvlərini də istifadə edə bilər. Amma parent classın private
üzvlərinə heç vaxt birbaşa müraciət edə bilməz. private
üzvlərə public
və ya protected
metodlar vasitəsilə dollayı yolla müraciət etmək olar:
class Fish { protected int size; private int age; public Fish(int age) { this.age = age; } public int getAge() { return age; } } class Shark extends Fish { private int number = 8; public Shark(int age) { super(age); this.size = 7; } public void displaySharkDetails() { System.out.println("Shark age: " + getAge()); // 1 System.out.println("Shark size: " + size); // 7 System.out.println("Shark number: " + number); // 8 } public static void main(String[] args) { new Shark(1).displaySharkDetails(); } }
Child classda biz parent classın public
metodunu (getAge()
) və protected
dəyişənini (size
) birbaşa istifadə edə bilərik. Ümumiyyətlə, varisliyə görə child class parent classın bütün üzvlərinə sahiblənir, yəni miras alır (access modifier`in icazə verdiyi üzvlər). Buna görə də biz child classda this açar sözündən istifadə edərək həm child classın, həm də parent classın üzvlərini çağıra bilərik. Yuxarıdakı metod ilə bu metod mahiyyət baxımından eynidir:
public void displaySharkDetails() { System.out.println("Shark age: " + this.getAge()); // 1 System.out.println("Shark size: " + this.size); // 7 System.out.println("Shark number: " + this.number); // 8 }
Biz parent classın üzvlərini super
açar sözünü istifadə edərək də çağıra bilərik, amma super
vasitəsilə cari classın üzvlərini çağıra bilmərik. Başqa sözlə this
və super
ilə həm parent class, həm də child classın üzvlərini çağıra bilərik, amma cari classın üzvlərini ancaq this
ilə çağırmaq mümkündür, əks halda compile xətası verəcək:
public void displaySharkDetails() { System.out.println("Shark age: " + super.getAge()); // 1 System.out.println("Shark size: " + super.size); // 7 System.out.println("Shark number: " + this.number); // 8 System.out.println("Shark number: " + super.number); // DOES NOT COMPILE }
MyExamCloud test suallarının içərisində təxminən belə bir nümunə ilə rastlaşmışdım, output`un nə olacağı soruşulurdu:
package packageA; public class A { protected int y = 10; }
package packageB; import packageA.A; public class B extends A { int y = 5; public void print() { A a = new A(); System.out.println(a.y + y); } }
İlk baxışdan sadə görünür. Yuxarıda da qeyd etdik ki, child class parent classın protected
üzvlərini istifadə edə bilər. Bu qaydaya istinad edərək suala yanaşdıqda ağlımıza gələn ilk cavab 15
-dir. Amma sualda çox incə bir nüans var, bəlkə də bu sertifikat sualları içərisində ən çaşdırıcı məqamlardan biridir. Əslində kod nümunəsi compile olunmur və səbəb protected
açar sözünün istifadəsi ilə bağlıdır. Əgər parent və child classlar fərqli paket(package)lərdədirsə, parent classdakı protected üzvlərə child classda ancaq child classın referansı vasitəsilə müraciət oluna bilər, parent classın öz referansı vasitəsilə müraciət edilə bilməz. Yuxarıdakı kod nümunəsində gördüyünüz kimi B
classında A
classının y
dəyişəninə A
classının öz referansı vasitəsilə müraciət edilir (a.y
), bu səbəbdən də bu sətir compile xətası verir. Əgər B
classının referansı vasitəsilə müraciət edilsə, xəta verməyəcək. Amma bu nümunədə həm A
, həm də B
classında istifadə edilən dəyişən adı eyni olduğundan burada y
dəyişənini B
classının referansı vasitəsilə çağırsaq, B
classına məxsus y
dəyişəninə müraciət ediləcək.
Yuxarıdakı kod nümunəsi üzərində biraz dəyişiklik edib, protected üzvlərə necə müraciət edə bilərik baxaq:
package packageA; public class A { protected int k = 10; protected int n = 15; }
package packageB; import packageA.A; public class B extends A { int k = 5; public void print() { A a = new A(); B b = new B(); System.out.println(k + b.k); // 10 System.out.println(k + super.k); // 15 System.out.println(k + b.n); // 20 System.out.println(k + a.n); // DOES NOT COMPILE } public static void main(String[] args) { B b = new B(); b.print(); } }
super() vs super
this
və this()
ifadlərinin bir-biri ilə əlaqəsi olmadığı kimi, super
və super()
ifadələri də bir-birindən tamamilə fərqlənir.
Düzgün istifadə qaydası:
public Book(int age) { // constructor super(); super.setAge (10); }
Səhv istifadə qaydası:
public Book(int age) { super; // DOES NOT COMPILE super().setAge (10); // DOES NOT COMPILE }
Aşağıdakı kod nümunəsini Enthuware testlərindən götürmüşəm, amma kodun daxilini biraz dəyişmişəm, daha maraqlı ola biləcək bir fakt əlavə etmişəm:
class Doll { String name; Doll(String nm) { this.name = nm; } } class Barbie extends Doll { // String name; // line1 Barbie(){ this(name); // DOES NOT COMPILE } Barbie(String nm){ super(name); // DOES NOT COMPILE } } public class Test { public static void main(String[] args) { Barbie b = new Barbie("mydoll"); } }
Bu sual həm də “Order of Initialization” mövzusu ilə əlaqəlidir, kodu compile etməyə cəhd etsək görəcəyik ki, compiler bizə “cannot reference name before supertype constructor has been called” xətasını göstərəcək. Əgər line1-i commentdən çıxarsaq kod yenə compile olunmayacaq, çünki super
constructor çağırılmamış name
dəyişəninə dəyər mənimsədilmir.
[topics lang=az]