CodeGym /Kurslar /JAVA 25 SELF /Sealed siniflər: sintaksis və tətbiqi

Sealed siniflər: sintaksis və tətbiqi

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

1. Sealed siniflərin sintaksisi: bu necə görünür

Gəlin Java-da OOP-un klassik bir probleminə başlayaq: açıq irs iyerarxiyası. Adi Java-da sinifiniz final elan edilməyibsə, istənilən kəs ondan miras ala bilər. Bu rahatdır, lakin bəzən gözlənilməzliklərə səbəb olur — məsələn, sinifinizin konkret hansı varisləri olacağını öncədən bilmirsiniz, deməli, switch və ya if-else-də bütün variantları işlədiyinizə zəmanət verə bilmirsiniz.

Nəticədə, obyektləri tipə görə işləyərkən (məsələn, switch-də pattern matching vasitəsilə) çox vaxt “hər ehtimala qarşı” default budağı əlavə edirsiniz: bəlkə kimsə haradasa yeni alt-sinf yaradıb?

Sealed siniflər bu problemi həll edir: varislərin siyahısını açıq şəkildə məhdudlaşdırmağa imkan verirlər. Bu, iyerarxiyanı qapalı və idarəolunan edir, kodunuzu isə daha proqnozlaşdırılan və təhlükəsiz edir.

Əsas sintaksis

Sealed siniflər Java 17-də peyda olub. Onlar sealed modifikatoru ilə elan olunur, icazə verilən varislər isə permits açar sözü ilə göstərilir:

public sealed class Shape permits Circle, Rectangle, Square {
    // Bütün fiqurlar üçün ümumi davranış
}

Burada biz Shape sinfini elan etmişik və yalnız Circle, RectangleSquare sinifləri onun birbaşa varisi ola bilər. Başqa heç kim Shape-i genişləndirə bilməyəcək — kompilyator buna icazə verməyəcək.

Vacibdir: permits-də göstərilən bütün siniflər eyni faylda elan edilməli və ya kompilyator üçün görünən olmalıdır (adətən eyni paketdə). Yeri gəlmişkən, əgər bütün varislər sealed sinfi ilə eyni faylda elan olunubsa, permits-i ümumiyyətlə yazmaya da bilərsiniz — kompilyator bunu özü anlayacaq.

Nümunə:

// Hər şey bir fayldadır - permits məcburi deyil
public sealed class Shape {
}
final class Circle extends Shape {}
final class Rectangle extends Shape {}

Varislərə tələblər

Hər bir varis öz statusunu açıq şəkildə göstərməlidir:

  • final olmaq (daha çox miras alınmasını qadağan edir),
  • və ya sealed olmaq (və irsi məhdudlaşdırmağa davam etmək),
  • və ya non-sealed olmaq (irsə icazə verir, məhdudiyyəti aradan qaldırır).

Nümunə:

public sealed class Shape permits Circle, Rectangle, Square {}

public final class Circle extends Shape {}
public sealed class Rectangle extends Shape permits FilledRectangle, EmptyRectangle {}
public non-sealed class Square extends Shape {}
  • Circle — yekun, daha miras almaq olmaz.
  • Rectangle — özü sealed-dir, yalnız iki alt-sinfə icazə verir.
  • Squarenon-sealed, istənilən kəs genişləndirə bilər.

Minimal nümunə

public sealed class Animal permits Dog, Cat {}

public final class Dog extends Animal {}
public final class Cat extends Animal {}

Yeni bir sinfi public class Wolf extends Animal {} kimi elan etməyə çalışın — kompilyasiya xətası alacaqsınız:

class Wolf is not allowed to extend sealed class Animal

2. Sealed siniflərin tətbiqi: harada və nə üçün istifadə etmək

Pattern matching və switch

Sealed siniflər xüsusilə switch-də pattern matching ilə yaxşı uyğunlaşır. Kompilyator mümkün bütün alt-sinifləri bildiyi üçün hər varianta emal etdiyinizi yoxlaya bilir və hətta default budağını tələb etmir.

public sealed interface Result permits Success, Error {}

public final class Success implements Result {
    public final String data;
    public Success(String data) { this.data = data; }
}

public final class Error implements Result {
    public final String message;
    public Error(String message) { this.message = message; }
}

public class Main {
    public static void main(String[] args) {
        Result result = new Success("Hurra!");
        switch (result) {
            case Success s -> System.out.println("Uğur: " + s.data);
            case Error e -> System.out.println("Səhv: " + e.message);
        }
    }
}

Kompilyator bilir ki, Result-un başqa variantı yoxdur və default tələb etmir. Əgər variantlardan birini emal etməyi unutsaq, kompilyator dərhal xəbərdar edəcək.

Nəzərə alın: Java 21-dən başlayaraq, əgər switch-də bütün variantlar emal olunmayıbsa, kompilyasiya xətası alacaqsınız. Daha əvvəlki versiyalarda (17–20) default tələb oluna bilər, lakin IDE yenə də qeyri-tam əhatə barədə xəbərdarlıq edəcək.

Təhlükəsizlik və nəzarət

Sealed siniflər inkişaf etdiriciyə iyerarxiyaya tam nəzarət imkanı verir. Bu, xüsusən variantların dəsti sabit olmalı olan domen modellərində vacibdir (məsələn, sifarişin vəziyyəti: New, Paid, Cancelled).

Kodu dəstəkləmək və inkişaf etdirmək daha asandır

Bütün varisləri bildikdə, yeni funksiyalar əlavə etmək, kodu dəstəkləmək və refaktor etmək asanlaşır. IDE də bütün variantlardan xəbərdar olacaq və avtomatik tamamlama və analizlə kömək edəcək.

3. Sealed siniflərdən istifadə üzrə praktiki nümunələr

Nümunə 1: Həndəsi fiqurlar

public sealed interface Shape permits Circle, Rectangle, Square {}

public final class Circle implements Shape {
    public final double radius;
    public Circle(double radius) { this.radius = radius; }
}

public final class Rectangle implements Shape {
    public final double width, height;
    public Rectangle(double width, double height) {
        this.width = width;
        this.height = height;
    }
}

public final class Square implements Shape {
    public final double side;
    public Square(double side) { this.side = side; }
}

İndi pattern matching ilə switch-dən təhlükəsiz istifadə edə bilərik:

Shape shape = new Circle(5);
switch (shape) {
    case Circle c -> System.out.println("Dairə: radius " + c.radius);
    case Rectangle r -> System.out.println("Düzbucaqlı " + r.width + "x" + r.height);
    case Square s -> System.out.println("Kvadrat: tərəf " + s.side);
}

Nümunə 2: Maliyyə tranzaksiyaları

public sealed interface Transaction permits Deposit, Withdraw, Transfer {}

public final class Deposit implements Transaction {
    public final double amount;
    public Deposit(double amount) { this.amount = amount; }
}

public final class Withdraw implements Transaction {
    public final double amount;
    public Withdraw(double amount) { this.amount = amount; }
}

public final class Transfer implements Transaction {
    public final double amount;
    public final String toAccount;
    public Transfer(double amount, String toAccount) {
        this.amount = amount;
        this.toAccount = toAccount;
    }
}

İndi emal edərkən heç bir tranzaksiya tipini unutmadığınıza əmin ola bilərsiniz:

Transaction tx = new Transfer(100, "ACC123");
switch (tx) {
    case Deposit d -> System.out.println("Depozit: " + d.amount);
    case Withdraw w -> System.out.println("Pul çıxarma: " + w.amount);
    case Transfer t -> System.out.println("Köçürmə: " + t.amount + " -> " + t.toAccount);
}

Nümunə 3: non-sealed ilə məhdud iyerarxiya

public sealed class Notification permits EmailNotification, SmsNotification, PushNotification {}

public final class EmailNotification extends Notification {}
public non-sealed class SmsNotification extends Notification {}
public final class PushNotification extends Notification {}

// İndi istənilən kəs SmsNotification-dan miras ala bilər
public class ViberNotification extends SmsNotification {}

4. Sealed siniflərin xüsusiyyətləri, məhdudiyyətləri və incəlikləri

Modifikatorlara tələblər

  • Sealed sinif mütləq bütün varisləri permits vasitəsilə açıq göstərməlidir.
  • Bütün varislər ya final, ya sealed, ya da non-sealed olmalıdır.
  • Varislər eyni faylda elan edilməli və ya kompilyator üçün görünən olmalıdır.

Abstrakt sealed siniflər

Sealed sinif həm abstrakt, həm interface, həm də adi sinif ola bilər. Məsələn:

public sealed abstract class Expr permits Const, Add, Mul {}

Digər modifikatorlarla uyğunluq

  • Sealed sinfi final və ya non-sealed kimi elan etmək olmaz.
  • interfacesealed ola bilər (və bu, çox rahatdır!).

Record sinifləri ilə istifadə

Record sinifləri sealed sinfin varisi ola bilər, əgər onlar final kimi elan olunublarsa (susmaya görə record həmişə final-dır):

public sealed interface Expr permits Const, Add, Mul {}

public record Const(int value) implements Expr {}
public record Add(Expr left, Expr right) implements Expr {}
public record Mul(Expr left, Expr right) implements Expr {}

5. Faydalı nüanslar

Sealed siniflər və pattern matching: bunun əlaqəsi

Sealed siniflərin əsas üstünlüyü — tam əhatəli pattern matching-dir. Qulağa qorxulu gələ bilər, işləməsi isə sadədir. Kompilyator bütün mümkün variantları bilir və siz switch-i default budağı olmadan yaza bilərsiniz:

Expr expr = ...;
switch (expr) {
    case Const c -> ...
    case Add a -> ...
    case Mul m -> ...
}

Əgər hansısa variantı emal etməsəniz, kompilyator layihəni yığmağa imkan verməyəcək — bu, çox rahat və təhlükəsizdir.

Real tapşırıqlarda tətbiq

Sealed siniflər harada həqiqətən faydalıdır? Variant dəsti sabit olduqda hər yerdə:

  • Əməliyyatın nəticəsi: Success, Error (yuxarıdakı nümunədə olduğu kimi).
  • Sifarişin vəziyyəti: New, Paid, Cancelled.
  • Parserlər üçün abstrakt sintaksis ağacları (AST).
  • API cavabları: Ok, NotFound, Error.
  • Sistemdə hadisələr: UserLoggedIn, UserLoggedOut, UserRegistered.

6. Sealed siniflərlə işləyərkən tipik səhvlər

Səhv №1: bütün varisləri permits-də göstərməmisiniz.
Əgər lazım olan bütün varisləri permits vasitəsilə sadalamamısınızsa, kompilyator dərhal şikayət edəcək. Məsələn, əgər permits Circle, Rectangle yazmısınız, amma Square-i unutmısınızsa və belə bir sinif mövcuddursa — xəta alacaqsınız.

Səhv №2: varis nə final, sealed nə də non-sealed-dir.
Əgər varis doğru modifikatorla elan edilməyibsə, kompilyator belə xəta verəcək: “Class must be either final, sealed or non-sealed”.

Səhv №3: varis başqa faylda elan edilib və kompilyatora görünmür.
permits-də sadalanan bütün siniflər kompilyator üçün əlçatan olmalıdır — ya eyni faylda, ya da eyni paketdə.

Səhv №4: switch-də bütün variantları emal etməmisiniz.
Əgər switch-də pattern matching ilə sealed sinifdən istifadə edirsinizsə və hansısa variantı unutmusunuzsa, kompilyator layihəni yığmağa imkan verməyəcək. Bu yaxşıdır — heç bir halı gözdən qaçırmayacaqsınız.

Səhv №5: permits-də olmayan sealed sinifdən miras almağa cəhd.
Əgər permits-də göstərilməyən varis sinfi yaratmağa çalışsanız, belə xəta alacaqsınız: “is not allowed to extend sealed class”.

Səhv №6: sealed sinifləri köhnə JDK versiyalarında istifadə etməyə cəhd.
Sealed siniflər yalnız Java 17-dən mövcuddur. Onları daha köhnə versiyada istifadə etməyə çalışsanız, kompilyasiya xətası alacaq və ya ümumiyyətlə layihəni yığa bilməyəcəksiniz.

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