10-bo‘lim
Vorislik va interfeyslar
extends va super, metodni qayta belgilash, abstract sinflar, interfeyslar va default metodlar, polimorfizm hamda vorislik o'rniga kompozitsiya.
Ushbu bo‘lim mundarijasi
Vorislik - mavjud sinf asosida yangisini qurish. Interfeys esa "nima qila olishi" ni belgilaydigan shartnoma.
Oddiy vorislik #
public class Vorislik {
public static void main(String[] args) {
new Talaba("Husanboy", 25, "Informatika").tanishtir();
new Oqituvchi("Nodira", 40, "Matematika").tanishtir();
}
}
class Shaxs {
protected final String ism;
protected final int yosh;
Shaxs(String ism, int yosh) {
this.ism = ism;
this.yosh = yosh;
}
void tanishtir() {
System.out.println("Men " + ism + ", " + yosh + " yoshdaman");
}
}
class Talaba extends Shaxs {
private final String yonalish;
Talaba(String ism, int yosh, String yonalish) {
super(ism, yosh);
this.yonalish = yonalish;
}
@Override
void tanishtir() {
super.tanishtir();
System.out.println(" " + yonalish + " yo'nalishi talabasiman");
}
}
class Oqituvchi extends Shaxs {
private final String fan;
Oqituvchi(String ism, int yosh, String fan) {
super(ism, yosh);
this.fan = fan;
}
@Override
void tanishtir() {
System.out.println("Men " + ism + ", " + fan + " o'qituvchisiman");
}
}
Men Husanboy, 25 yoshdaman
Informatika yo'nalishi talabasiman
Men Nodira, Matematika o'qituvchisiman
| Kalit so'z | Ma'nosi |
|---|---|
extends Shaxs | Shu sinfdan meros oladi |
super(...) | Ota-ona konstruktorini chaqiradi |
super.metod() | Ota-ona versiyasini chaqiradi |
@Override | Qayta belgilanayotganini bildiradi |
protected | Vorislar ko'radi, tashqaridagilar yo'q |
super(...) birinchi bo'lishi shartVoris konstruktorida super(...) eng birinchi ko'rsatma bo'lishi
kerak.
Yozmasangiz, Java super() ni o'zi qo'shadi - ya'ni ota-onaning
parametrsiz konstruktorini chaqiradi. Agar unday konstruktor
bo'lmasa, kompilyatsiya xatosi chiqadi:
error: constructor Shaxs in class Shaxs cannot be applied
to given types
Bu yangi boshlovchilar tez-tez uchraydigan holat: ota-onada faqat
parametrli konstruktor bor, vorisda esa super(...) yozilmagan.
Bu C# dan muhim farq. C# da metodni virtual deb belgilash kerak,
Java'da esa hamma metod avtomatik qayta yozilishi mumkin.
Qayta yozishni taqiqlash uchun final yoziladi:
class Asos {
final void ozgarmas() { }
}
Bu shuni anglatadiki, Java'da sinf yozganingizda vorislar har qanday
metodni o'zgartirishi mumkin - siz buni rejalashtirmagan bo'lsangiz
ham. Shuning uchun vorislikka mo'ljallanmagan sinflarni final deb
belgilash yaxshi amaliyot.
abstract sinf #
public class Shakllar {
public static void main(String[] args) {
Shakl[] shakllar = {new Doira(5), new Togritortburchak(4, 6)};
for (Shakl shakl : shakllar) {
System.out.printf("%-16s maydoni = %.2f%n",
shakl.nomi(), shakl.maydon());
}
}
}
abstract class Shakl {
abstract String nomi();
abstract double maydon();
}
class Doira extends Shakl {
private final double radius;
Doira(double radius) {
this.radius = radius;
}
@Override
String nomi() {
return "Doira";
}
@Override
double maydon() {
return Math.PI * radius * radius;
}
}
class Togritortburchak extends Shakl {
private final double eni;
private final double boyi;
Togritortburchak(double eni, double boyi) {
this.eni = eni;
this.boyi = boyi;
}
@Override
String nomi() {
return "To'rtburchak";
}
@Override
double maydon() {
return eni * boyi;
}
}
Doira maydoni = 78.54
To'rtburchak maydoni = 24.00
abstract sinfdan obyekt yaratib bo'lmaydi; abstract metodning
tanasi yo'q va voris uni albatta amalga oshirishi shart.
Sikl Shakl turi bilan ishlaydi, lekin har bir element uchun
o'zining maydon() metodi chaqirildi.
Kod qaysi aniq shakl kelishini bilmaydi. Yangi shakl qo'shsangiz - masalan uchburchak - bu siklni umuman o'zgartirish kerak emas.
Bu obyektga yo'naltirilgan dasturlashning asosiy foydasi.
Diqqat: printf da %n ishlatilgan, \n emas. %n har bir
tizimga mos qator uzilishini qo'yadi - Windows'da \r\n, Linux'da
\n. Ko'chma kod uchun shu afzal.
Interfeys #
import java.util.ArrayList;
import java.util.List;
public class Interfeyslar {
public static void main(String[] args) {
Yozuvchi[] chiqishlar = {new Konsolga(), new Xotiraga()};
for (Yozuvchi y : chiqishlar) {
y.yoz("Salom");
y.yoz("Dunyo");
}
for (Yozuvchi y : chiqishlar) {
if (y instanceof Xotiraga x) {
x.korsat();
}
}
}
}
interface Yozuvchi {
void yoz(String matn);
default void yozKatta(String matn) {
yoz(matn.toUpperCase());
}
}
class Konsolga implements Yozuvchi {
@Override
public void yoz(String matn) {
System.out.println("[konsol] " + matn);
}
}
class Xotiraga implements Yozuvchi {
private final List<String> qatorlar = new ArrayList<>();
@Override
public void yoz(String matn) {
qatorlar.add(matn);
}
void korsat() {
System.out.println("[xotira] " + qatorlar.size()
+ " qator: " + String.join(", ", qatorlar));
}
}
[konsol] Salom
[konsol] Dunyo
[xotira] 2 qator: Salom, Dunyo
publicInterfeysdagi metodlar sukut bo'yicha public abstract hisoblanadi.
Shuning uchun ularni amalga oshirganda public yozish
majburiy - aks holda kirish darajasi torayadi va kompilyator
xato beradi:
error: yoz(String) in Konsolga cannot implement yoz(String)
in Yozuvchi; attempting to assign weaker access privileges
Bu juda ko'p uchraydigan xato: sinf metodlarida public yozmaslikka
odatlangan dasturchi interfeysni amalga oshirganda ham unutadi.
default metodlar #
Java 8 dan interfeysda tayyor kod ham bo'lishi mumkin:
public class Default {
public static void main(String[] args) {
Salomlovchi s = ism -> System.out.println("Salom, " + ism);
s.salomlash("Husanboy");
s.salomlashRasmiy("Nodira Karimovna");
}
}
interface Salomlovchi {
void salomlash(String ism);
default void salomlashRasmiy(String ism) {
salomlash("hurmatli " + ism);
}
}
Salom, Husanboy
Salom, hurmatli Nodira Karimovna
default nima uchun qo'shilganJava 8 da List interfeysiga forEach va stream metodlari
qo'shildi.
Muammo: dunyodagi minglab sinf List ni amalga oshirgan edi. Yangi
metod qo'shilsa, ularning hammasi buzilardi.
default shu muammoni hal qildi: interfeysga tayyor tanali metod
qo'shish mumkin bo'ldi va mavjud sinflar o'zgarishsiz ishlashda
davom etdi.
Ya'ni default - avvalo moslikni saqlash vositasi. Undan
o'zingizning interfeyslaringizda ehtiyotkorlik bilan foydalaning.
Bir nechta interfeys #
Sinf bitta sinfdan meros oladi, lekin istalgancha interfeysni amalga oshirishi mumkin:
public class KopInterfeys {
public static void main(String[] args) {
Hujjat h = new Hujjat("Hisobot");
h.saqla();
h.chop();
}
}
interface Saqlanadigan {
void saqla();
}
interface ChopEtiladigan {
void chop();
}
class Hujjat implements Saqlanadigan, ChopEtiladigan {
private final String nom;
Hujjat(String nom) {
this.nom = nom;
}
@Override
public void saqla() {
System.out.println(nom + " saqlandi");
}
@Override
public void chop() {
System.out.println(nom + " chop etildi");
}
}
Hisobot saqlandi
Hisobot chop etildi
Bir nechta sinfdan meros olish "olmos muammosi" ni keltirib chiqaradi: ikkita ota-onada bir xil nomli metod bo'lsa, qaysi biri ishlashi noaniq bo'lib qoladi.
C++ da bu mumkin va shu sababli murakkab qoidalar kerak. Java bu yo'ldan bormaslikka qaror qilgan: bitta sinf, istalgancha interfeys.
default metodlar paydo bo'lgach, muammo qisman qaytdi - ikkita
interfeysda bir xil nomli default metod bo'lsa, sinf uni
majburan qayta belgilashi kerak. Kompilyator buni talab qiladi.
Interfeys yoki abstract sinf? #
| Interfeys | Abstract sinf | |
|---|---|---|
| Nechtasini olish mumkin | Istalgancha | Bittasini |
| Maydon saqlay oladimi | Faqat static final | Ha |
| Konstruktor | Yo'q | Ha |
| Tayyor kod | default metodlar | Ha, to'liq |
| Qachon | "Nima qila oladi" | "Nima ekanligi" |
Qoida: "bu shunday qila oladi" desangiz - interfeys
(Yozuvchi). "bu shunday narsa" desangiz - abstract sinf
(Shakl).
Turni tekshirish #
public class Tekshirish {
public static void main(String[] args) {
Object[] narsalar = {new Doira(2), new Kvadrat(3), "matn"};
for (Object narsa : narsalar) {
if (narsa instanceof Shakl shakl) {
System.out.printf("Shakl, maydoni %.2f%n", shakl.maydon());
} else {
System.out.println("Shakl emas: "
+ narsa.getClass().getSimpleName());
}
}
}
}
abstract class Shakl {
abstract double maydon();
}
class Doira extends Shakl {
private final double r;
Doira(double r) {
this.r = r;
}
@Override
double maydon() {
return Math.PI * r * r;
}
}
class Kvadrat extends Shakl {
private final double t;
Kvadrat(double t) {
this.t = t;
}
@Override
double maydon() {
return t * t;
}
}
Shakl, maydoni 12.57
Shakl, maydoni 9.00
Shakl emas: String
Vorislik o'rniga kompozitsiya #
Vorislik faqat "A - bu B ning turi" bo'lganda to'g'ri:
Talaba- buShaxsning turi. To'g'ri.Kvadrat- buShaklning turi. To'g'ri.Avtomobil- buDvigatelning turi? Yo'q. Avtomobilda dvigatel bor.
Ikkinchi holatda vorislik emas, kompozitsiya kerak:
public class Kompozitsiya {
public static void main(String[] args) {
new Avtomobil("Nexia").yur();
}
}
class Dvigatel {
String ishga() {
return "dvigatel ishga tushdi";
}
}
class Avtomobil {
private final String nom;
private final Dvigatel dvigatel = new Dvigatel();
Avtomobil(String nom) {
this.nom = nom;
}
void yur() {
System.out.println(nom + ": " + dvigatel.ishga());
}
}
Nexia: dvigatel ishga tushdi
Bu moslashuvchanroq: dvigatelni keyin boshqasiga almashtirish mumkin,
Avtomobil esa o'zgarmaydi.
Amaliy qoida: avval kompozitsiyani o'ylang, vorislikni faqat haqiqatan "bu shuning turi" bo'lganda ishlating.
ShaxssinfidanTalabavaOqituvchini meros oling.super(...)ni olib tashlab, kompilyator nima deyishini ko'ring.- Metodni
finalqilib, vorisda qayta yozishga urinib ko'ring. abstractShaklsinfi va uchta vorisini yozing.- Ularni bitta massivga solib, siklda maydonlarini chiqaring.
- Interfeysni amalga oshirganda
publicni unutib, xatoni ko'ring. defaultmetodli interfeys yozing.- Bitta sinfda ikkita interfeysni amalga oshiring.
instanceofbilan turni tekshirib, xavfsiz aylantiring.AvtomobilvaDvigatelmisolida kompozitsiyani qo'llang.
Xulosa #
- Vorislik
extends, interfeysimplementsbilan yoziladi. super(...)voris konstruktorining birinchi ko'rsatmasi bo'lishi shart.- Java'da metodlar sukut bo'yicha qayta yoziladi; taqiqlash uchun
final. @Overrideni har doim yozing - nom xato bo'lsa kompilyator aytadi.abstractsinfdan obyekt yaratib bo'lmaydi.- Interfeys metodlarini amalga oshirganda
publicmajburiy. defaultmetodlar mavjud kodni buzmasdan interfeysni kengaytirish uchun qo'shilgan.printfda%nishlating,\nemas.- Munosabat "bu shuning turi" bo'lmasa - kompozitsiya.
Keyingi bo'limda record, enum va yopiq sinflarni ko'ramiz.
O‘qish tarixini saqlamoqchimisiz?
Tizimga kirsangiz, tugatgan bo‘limlaringiz saqlanadi va qoldirgan joyingizdan davom etasiz.
Xatolik topdingizmi?
Imlo xatosi, ishlamaydigan kod yoki noto‘g‘ri ma‘lumotni ko‘rsangiz - bizga xabar bering. Har bir xabar administrator tomonidan ko‘rib chiqiladi.