Видове класове: record и enum

Класът е основният механизъм за дефиниране на потребителски типове данни в Java. Освен обикновените класове езикът предоставя специализирани конструкции, които също дефинират типове. Такива конструкции са record и enum.

Обикновеният клас се използва, когато трябва да се опишат данни и поведение с пълен контрол върху полетата, конструкторите и методите. Записът record се използва основно за компактно представяне на данни. Изброеният тип enum се използва за фиксиран набор от допустими стойности.

Записи (record)

Записът record е специален вид клас, предназначен за моделиране на обекти, които основно съхраняват данни. Той е подходящ, когато даден тип трябва да съдържа няколко стойности и не се очаква тези стойности да се променят след създаване на обекта.

Обикновеният клас изисква ръчно деклариране на полета, конструктор, методи за достъп и често методи като toString(), equals() и hashCode(). При record голяма част от този код се генерира автоматично от компилатора.

Декларация на record

record Student(String name, int facultyNumber) {

}

В този пример Student е запис с два компонента:

  • name от тип String;
  • facultyNumber от тип int.

Компонентите в заглавната част на record описват данните, които записът съхранява. За всеки компонент компилаторът автоматично създава частно финално поле и публичен метод за достъп.

Създаване на обект от record

Обект от record се създава чрез ключовата дума new, както при обикновен клас.

Student student = new Student("Ivan Petrov", 12345);

При създаването се извиква автоматично генерираният конструктор. Подадените аргументи трябва да съответстват по ред, тип и брой на компонентите в декларацията на record.

Достъп до стойностите

При record методите за достъп не използват стандартната форма getName(). Вместо това всеки метод има същото име като съответния компонент.

System.out.println(student.name());
System.out.println(student.facultyNumber());

Методът name() връща стойността на компонента name. Методът facultyNumber() връща стойността на компонента facultyNumber.

Автоматично генерирани елементи

За един record компилаторът автоматично създава:

  • частни финални полета за компонентите;
  • конструктор, който приема стойности за всички компоненти;
  • методи за достъп до компонентите;
  • метод toString();
  • метод equals(Object other);
  • метод hashCode().

Това означава, че два обекта от един и същ record могат да бъдат сравнявани по стойност чрез equals(). При обикновен клас такова поведение трябва да се дефинира ръчно, ако е необходимо.

record Point(int x, int y) {

}

public class Application {

    public static void main(String[] args) {
        Point first = new Point(10, 20);
        Point second = new Point(10, 20);

        System.out.println(first.equals(second));
        System.out.println(first);
    }
}

Резултатът от first.equals(second) е true, защото двата обекта са от един и същ record тип и съдържат еднакви стойности в компонентите си.

Неизменимост на record

Компонентите на record са неизменими след създаване на обекта. Това означава, че стойностите се задават чрез конструктора и след това не могат да бъдат променяни чрез присвояване.

record Product(String name, double price) {

}

Product product = new Product("Keyboard", 59.90);

// product.price = 49.90; // не е позволено

Тази особеност прави record подходящ за пренасяне на данни между различни части на програмата, когато не трябва да се допуска случайна промяна на състоянието.

Компактен конструктор в record

В record може да се дефинира компактен конструктор. Той се използва, когато при създаване на обекта трябва да се изпълни допълнителен код, без да се изписва пълният списък с параметри.

record Product(String name, double price) {

    Product {
        name = name.trim();
    }
}

В компактния конструктор не се присвояват ръчно стойности към полетата. След изпълнение на допълнителния код компилаторът автоматично записва параметрите в съответните компоненти.

Кога се използва record

record е подходящ, когато:

  • типът основно съхранява данни;
  • всички основни стойности са известни при създаване на обекта;
  • не е необходима промяна на стойностите след създаване;
  • трябва да се намали шаблонният код за конструктор, достъп до стойности и сравнение.

record не е подходящ, когато обектът трябва да има променливо състояние, сложен жизнен цикъл или много операции, които променят вътрешните му данни.

Изброени типове (enum)

Изброеният тип enum е специален вид клас, който дефинира фиксиран набор от именувани константи. Той се използва, когато дадена стойност трябва да бъде избрана само измежду предварително известни допустими стойности.

Пример:

enum Status {

    NEW,
    IN_PROGRESS,
    COMPLETED,
    CANCELLED

}

В този пример Status е тип, а NEW, IN_PROGRESS, COMPLETED и CANCELLED са неговите допустими стойности. Не може да се създаде произволна стойност от тип Status, която не е описана в декларацията на enum.

Използване на enum

Status status = Status.NEW;

Стойностите на enum се достъпват чрез името на типа и името на константата. Това прави кода по-четим и намалява риска от грешки, които биха възникнали при използване на обикновени текстови стойности.

if (status == Status.COMPLETED) {
    System.out.println("The task is completed.");
}

Стойностите на enum могат да се сравняват чрез оператора ==, защото всяка константа е единствена предварително създадена инстанция на съответния изброен тип.

enum вместо текстови стойности

Без enum често се използват низове.

String status = "COMPLETED";

Този подход допуска грешки, защото компилаторът не може да провери дали текстът е валиден статус.

String status = "COMPLETEDD"; // технически валиден String, но невалиден статус

При enum подобна грешка се открива още при компилация.

Status status = Status.COMPLETED;

Вградени методи на enum

Метод Предназначение
values() Връща масив с всички константи.
valueOf(String) Връща константа по име.
ordinal() Връща поредния номер на константата.
name() Връща името на константата.

Пример:

for (Status status : Status.values()) {
    System.out.println(status);
}

Методът values() връща всички константи в реда, в който са декларирани. Методът valueOf(String) връща константа по точно име. Ако такова име не съществува, се получава грешка по време на изпълнение.

Status status = Status.valueOf("NEW");
System.out.println(status.name());
System.out.println(status.ordinal());

Методът name() връща името на константата като текст. Методът ordinal() връща позицията на константата, започвайки от 0. Не се препоръчва програмната логика да зависи от ordinal(), защото промяна в реда на константите променя и числовите им позиции.

Полета, конструктори и методи в enum

Изброеният тип може да съдържа полета, конструктори и методи. Това е възможно, защото enum е специален вид клас.

enum UserRole {

    ADMIN("Administrator"),
    MODERATOR("Moderator"),
    USER("Standard user");

    private final String description;

    UserRole(String description) {
        this.description = description;
    }

    String getDescription() {
        return description;
    }
}

В този пример всяка константа има собствена стойност за description. Конструкторът на enum се извиква автоматично при създаване на константите. Той не се извиква с new от външен код.

System.out.println(UserRole.ADMIN.getDescription());

Особености на enum

Конструкторът на enum не може да бъде публичен, защото константите са предварително определени в самата декларация. Нови стойности не могат да се създават извън изброения тип.

Използването на enum:

  • ограничава допустимите стойности;
  • подобрява четимостта на програмния код;
  • осигурява проверка на типовете още при компилация;
  • намалява вероятността от грешки.

enum е подходящ за статус на поръчка, ден от седмицата, сезон, вид плащане, потребителска роля или друг ограничен набор от стойности.

Сравнение между class, record и enum

Конструкция Основно предназначение Създаване на стойности
class Описание на обекти с данни и поведение. Чрез new, според дефинираните конструктори.
record Компактно описание на неизменими обекти за данни. Чрез new, чрез автоматично генериран конструктор.
enum Описание на фиксиран набор от именувани стойности. Само чрез константите, декларирани в enum.

This site uses Just the Docs, a documentation theme for Jekyll.