1. record və class müqayisəsi: əsas fərqlər nələrdir?
Java-da məlumat tiplərini təsvir etməyin iki əsas yolu var: adi siniflər (class) və record sinifləri (record). İlk baxışda hər iki yanaşma məlumatları saxlamağa və emal etməyə imkan verir. Amma bir az dərinə getsək, fərqlərin göründüyündən daha çox olduğunu görəcəyik!
Fərqlər cədvəli: class vs record
| Xüsusiyyət | Adi sinif (class) | Record sinfi (record) |
|---|---|---|
| Dəyişkənlik | İstənilən: sahələri final etmək də olar, etməmək də | Dəyişməz: bütün sahələr final |
| İrsiyyət | İrs almaq olar (extends), susmaya görə final deyil | Həmişə final, superklass ola bilməz |
| Sahələr | İstənilən: statik, qeyri-statik, final və ya qeyri-final, istənilən tiplər | Yalnız record komponentləri (private final), üstəlik statik sahələr |
| Getter/setter-lər | Özümüz yazırıq (və ya Lombok ilə generasiya edirik) | Getter-lər avtomatik yaradılır (sahənin adı metodun adı olur), setter yoxdur |
| equals/hashCode/toString | Adətən əl ilə yazırıq/generasiya edirik (equals, hashCode, toString) | Bütün komponentlər üzrə avtomatik generasiya olunur |
| Konstruktorlar | İstənilən, say məhdudiyyəti yoxdur | Bir əsas (bütün komponentlər üzrə), kompakt konstruktor əlavə etmək olar |
| İnterfeyslər | Reallaşdırmaq olar | Reallaşdırmaq olar |
| Əlavə metodlar | İstənilən | Əlavə etmək olar, amma yalnız metodlar (sahələr yox) |
| Kolleksiyalarda istifadə | Olar, amma equals/hashCode düzgün reallaşdırılmalıdır | Açarlar/dəyərlər üçün ideal, hər şey artıq reallaşdırılıb |
Aydınlıq üçün nümunə
Adi sinif:
public class Person {
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
@Override
public boolean equals(Object o) { /* ... */ }
@Override
public int hashCode() { /* ... */ }
@Override
public String toString() { /* ... */ }
}
Record sinfi:
public record Person(String name, int age) { }
Vəssalam! Bir sətir kod — və eyni nəticəni (hətta daha yaxşısını) alırıq. Və vacib nəyisə reallaşdırmağı unutmaq riski yoxdur.
2. record siniflərinin məhdudiyyətləri
Record sinifləri sadəcə “qısa sintaksis” deyil, öz sərt qaydaları olan ayrıca bir konsepsiyadır. Gəlin onları daha ətraflı nəzərdən keçirək.
Record həmişə final-dır
Tərifə görə record sinfi həmişə final-dır. Bu o deməkdir ki, siz record-dan subklass yarada bilməzsiniz:
public record Point(int x, int y) { }
// public class ColoredPoint extends Point { } // Kompilyasiya xətası!
Davranışı genişləndirmək lazımdırsa, adi siniflərdən və ya kompozisiyadan istifadə edin (record-u sinifin içinə daxil edin).
Record superklass ola bilməz
Record sinfi digər siniflər üçün valideyn ola bilməz, o həmişə final-dır. Bu məntiqlidir: əgər bu mümkün olsaydı, kimsə dəyişən sahə əlavə edə bilərdi — və “dəyişməz məlumatlar” konsepsiyası dağılardı.
Yalnız final sahələr (komponentlər)
Bütün record komponentləri başlıqda elan olunur və susmaya görə private final-dır. record-un gövdəsinə qeyri-statik sahə əlavə edə bilməzsiniz:
public record User(String login, String email) {
// int counter; // Xəta! Qeyri-statik sahələr əlavə etmək olmaz
static int totalUsers = 0; // Olar, bu statik sahədir
}
Setter-lər yoxdur
Record sinfinin komponentləri üçün setter-lər ola bilməz. setX(int x) kimi metod əlavə etmək cəhdi mənasızdır: obyekt yaradıldıqdan sonra sahənin dəyərini dəyişə bilməyəcəksiniz.
public record Point(int x, int y) {
// public void setX(int x) { this.x = x; } // Xəta: final sahəni dəyişmək olmaz
}
Boş konstruktor yoxdur
record-sinifdə həmişə yalnız əsas konstruktor var, o da bütün komponentlər üçün dəyərləri qəbul edir. Bütün məlumatlar göstərilmədən record yaratmaq olmaz:
Point p = new Point(1, 2); // OK
// Point p = new Point(); // Xəta: parametrsiz konstruktor yoxdur
Qeyri-statik init-bloklar yoxdur
Record sinfi qeyri-statik inisializator blokları (metodlardan kənar, fiqurlu mötərizələrdə yazılan) tuta bilməz:
public record User(String login) {
// { /* ... */ } // Xəta: qeyri-statik inisializatorlar qadağandır
}
İrsiyyət üzrə məhdudiyyətlər
Record sinfi açıq şəkildə başqa sinfi miras ala bilməz (bütün record-lar üçün gizli baza sinfi olan java.lang.Record istisnadır). Amma interfeysləri reallaşdırmaq — buyurun!
public interface Printable {
void print();
}
public record Book(String title) implements Printable {
@Override
public void print() {
System.out.println("Kitabı çap edirik: " + title);
}
}
Mürəkkəb biznes məntiqi üçün uyğun deyil
record — məlumat üçündür, davranış üçün yox. Obyektinizin mürəkkəb məntiqi, dəyişən vəziyyəti, “həyat dövrü” və ya çoxlu asılılıqları varsa — record kömək etməyəcək. Daha yaxşısı adi sinifdən istifadə etməkdir.
3. record siniflərini nə vaxt istifadə etməli?
- DTO (Data Transfer Object): tətbiqin qatları, servislər, mikroservislər və ya REST nəzarətçiləri arasında dəyişməz məlumatları ötürmək üçün (məsələn, JSON cavablarında).
- Value Object: yalnız dəyərləri ilə müəyyən olunan obyektlər.
- Kolleksiyalarda açarlar və dəyərlər: equals və hashCode-un düzgün reallaşdırılması vacib olduqda (məsələn, HashMap və ya Set üçün).
- Hesablama nəticələri: metoddən bir neçə dəyəri birdən qaytarmaq lazım olduqda (məsələn, record Pair<T, U>(T first, U second)).
Nümunə: REST nəzarətçisi üçün DTO
public record UserDto(String login, String email) { }
İndi bu tipdən olan obyekti nəzarətçidən rahatlıqla qaytara bilərsiniz — kimsənin onun sahələrini dəyişəcəyindən narahat olmayaraq.
Nümunə: HashMap üçün açar
public record Point(int x, int y) { }
Map<Point, String> pointNames = new HashMap<>();
pointNames.put(new Point(1, 2), "A");
pointNames.put(new Point(3, 4), "B");
// Hər şey düzgün işləyir: equals və hashCode artıq reallaşdırılıb!
4. record siniflərindən nə vaxt İSTİFADƏ ETMƏMƏK lazımdır
- Dəyişən vəziyyət: obyekt yaradıldıqdan sonra azı bir sahə dəyişməlidirsə.
- Mürəkkəb məntiq: obyektin davranışı mürəkkəbdirsə, çoxlu metodları və dəyişən vəziyyətli iç-içə obyektləri varsa.
- İrsiyyət: sinif iyerarxiyası, abstrakt baza sinifləri, metodların yenidən yazılması tələb olunursa.
- Biznes-məntiq entitiləri: məsələn, verilənlər bazasında “yaşayan” və unikal identifikatora malik obyektlər.
Nümunə: nə vaxt adi sinif lazımdır
public class Account {
private String id;
private int balance;
public Account(String id, int balance) {
this.id = id;
this.balance = balance;
}
public void deposit(int amount) { balance += amount; }
public void withdraw(int amount) { balance -= amount; }
// getters, setters, equals, hashCode, toString...
}
Burada obyektin vəziyyətinin dəyişdiyi aydın görünür — record uyğun deyil.
5. Praktik nümunələr: record və class arasında seçim
Nümunə 1: record — ideal seçim
public record Rectangle(int width, int height) {
public int area() {
return width * height;
}
}
- Düzbucaqlı yalnız en və hündürlük ilə müəyyən olunur.
- Yaradıldıqdan sonra bu dəyərləri dəyişməyə ehtiyac yoxdur.
- Faydalı area() metodunu əlavə etmək olar.
- Qalan hər şeyi Java sizin üçün edəcək.
Nümunə 2: class — ən yaxşı seçim
public class MutableRectangle {
private int width;
private int height;
public MutableRectangle(int width, int height) {
this.width = width;
this.height = height;
}
public void setWidth(int width) { this.width = width; }
public void setHeight(int height) { this.height = height; }
public int area() { return width * height; }
}
Düzbucaqlının ölçülərini yaradıldıqdan sonra dəyişmək lazımdır? Adi sinifdən istifadə edin.
6. record sinifləri ilə işləyərkən tipik səhvlər
Səhv №1: qeyri-statik sahə əlavə etmək cəhdi.
Record sinfi komponentlər siyahısından kənarda qeyri-statik sahələr elan etməyə imkan vermir. Belə cəhd etsəniz — kompilyator xəta verəcək. Məsələn:
public record City(String name) {
// int population; // Xəta!
}
Səhv №2: setter əlavə etmək istəyi.
Record komponentlər üçün setter-ləri dəstəkləmir. Obyekt yaradıldıqdan sonra sahənin dəyərini dəyişmək cəhdi — kompilyasiya səhvidir.
Səhv №3: record-u miras almaq və ya record-dan miras almağa cəhd.
Record həmişə final-dır. record-dan miras almaq olmaz və record başqa sinfi miras ala bilməz (gizli java.lang.Record istisna olmaqla).
Səhv №4: dəyişən (mutable) obyektlər üçün record istifadə etmək.
Obyektin vəziyyətini yaratdıqdan sonra dəyişməyi planlaşdırırsınızsa — record sizin üçün deyil! Adi sinifdən istifadə edin.
Səhv №5: konstruktor məhdudiyyətlərini unutmaq.
Record sinfində mütləq bütün komponentləri qəbul edən konstruktor olmalıdır. Parametrsiz konstruktor yoxdur!
GO TO FULL VERSION