C, PHP, VB, .NET

Дневникът на Филип Петров


* Ограничено наследими (sealed) класове

Публикувано на 29 май 2023 в раздел ПИК3 Java.

Преди Java 17 в езика се следваше подход за наследяване, в който или всеки интерфейс/клас може да бъде разширяван (наследяван) от всеки с extends, или това е забранено чрез ключова дума final. С включването на новата ключова дума sealed се въвеждат ограничено наследими класове. Те дават разрешителен списък, с който се изреждат ограничено количество интерфейси/класове от същия пакет, които могат да имплементират/наследяват текущия.

Нека разгледаме следния пример. Ще дефинираме интерфейс „четириъгълник“ и ще реализираме негови реализации „правоъгълник“ и „успоредник“.

package figures2d;

abstract interface Quadrilateral{
    double getArea();
    double getPerimeter();
}

record Rectangle (double sideA, double sideB) implements Quadrilateral{
    @Override
    public double getArea(){
        return sideA*sideB;
    }
    @Override
    public double getPerimeter(){
        return 2*sideA+2*sideB;
    }
}

record Parallelogram (double sideA, double sideB, double angle) implements Quadrilateral{
    @Override
    public double getArea(){
        return sideA*sideB*Math.sin(angle);
    }
    @Override
    public double getPerimeter(){
        return 2*sideA+2*sideB;
    }
}

Тук никой не ни спира да направим например клас „равностранен триъгълник“, който имплементира „четириъгълник“ - това би било напълно нелогично и нежелано, но все пак е възможно. С ключова дума sealed защитаваме интерфейса така, че изреждаме само и единствено класовете, които имат право да го имплементират:

sealed interface Quadrilateral permits Rectangle, Parallelogram{
    double getArea();
    double getPerimeter();
}

record Rectangle (double sideA, double sideB) implements Quadrilateral{
    @Override
    public double getArea(){
        return sideA*sideB;
    }
    @Override
    public double getPerimeter(){
        return 2*sideA+2*sideB;
    }
}

record Parallelogram (double sideA, double sideB, double angle) implements Quadrilateral{
    @Override
    public double getArea(){
        return sideA*sideB*Math.sin(angle);
    }
    @Override
    public double getPerimeter(){
        return 2*sideA+2*sideB;
    }
}

Вече няма да можем да правим нови класове с имплементации на интерфейса Quadrilateral.

В случая използваме record, с което скриваме част от важните ограничения - класът, който имплементира защитения интерфейс или наследява защитен клас трябва изрично да окаже от своя страна дали е или final (ненаследим), sealed (частично наследим) или свободен (non-sealed). Следният пример демонстрира това:

sealed interface Quadrilateral permits Parallelogram{
    double getArea();
    double getPerimeter();
}

sealed class Parallelogram implements Quadrilateral permits Rectangle{
    double sideA;
    double sideB;
    double angle;
    public Parallelogram(double sideA, double sideB, double angle){
        this.sideA = sideA;
        this.sideB = sideB;
        this.angle = angle;
    }
    @Override
    public double getArea(){
        return sideA*sideB*Math.sin(angle);
    }
    @Override
    public double getPerimeter(){
        return 2*sideA+2*sideB;
    }
}

sealed class Rectangle extends Parallelogram permits Square{
    public Rectangle(double sideA, double sideB){
        super(sideA, sideB, Math.PI/2);
    }
}

final class Square extends Rectangle{
    public Square(double side){
        super(side, side);
    }
}

Ако не се посочи какъв тип (sealed, non-sealed или final) ще е имплементиращият/наследяващият клас, компилатора ще даде грешка.

 



Добави коментар

Адресът на електронната поща няма да се публикува


*