1. Objects sinfi ilə tanışlıq
Mövzuya keçək! Əgər dəyərləri əl ilə null-a qarşı yoxlamaqdan və hər sahə üçün hash hesabı yazmaqdan yorulmusunuzsa, utilit sinif java.util.Objects köməyinizə çatacaq. Onun məqsədi — obyektlərlə işi daha sadə, yığcam və təhlükəsiz etmək.
Bu, həqiqətən, “isveçrə bıçağı”dır: sinif obyektləri bərabərlik üzrə təhlükəsiz müqayisə edə bilir (risksiz, yəni NullPointerException almadan), hash-kodları rahat hesablaya bilir, komparator vasitəsilə müqayisə edir və arqumentləri null-a qarşı yoxlayır.
Objects.equals: null nəzərə alınmaqla təhlükəsiz müqayisə
Sadəcə a.equals(b) yazsanız və a null olsa, NullPointerException alacaqsınız. Əl ilə yoxlamalar həcmli olur. Objects.equals(a, b) bunu sizin yerinizə edir:
- Hər ikisi null-dırsa — true qaytarır.
- Yalnız biri null-dırsa — false qaytarır.
- Hər ikisi null deyilsə — adi equals çağırılır.
import java.util.Objects;
String a = null;
String b = "Java";
System.out.println(Objects.equals(a, b)); // false
String c = null;
System.out.println(Objects.equals(a, c)); // true
String d = "Java";
String e = "Java";
System.out.println(Objects.equals(d, e)); // true
Bu niyə rahatdır? Kod daha qısadır, daha təmizdir və təsadüfi NPE-lərdən qorunur.
2. Objects.hash və hashCode: hash-ın yığcam hesablanması
hashCode-u equals ilə birlikdə yenidən təyin edərkən, xüsusən sahələr çox olanda, səhv etmək asandır. Əl ilə yazılmış kod çox vaxt həcmli və kövrək görünür:
@Override
public int hashCode() {
int result = 17;
result = 31 * result + (name != null ? name.hashCode() : 0);
result = 31 * result + age;
return result;
}
Objects.hash metodu problemi həll edir — qısa, təhlükəsiz və null dəstəyi ilə:
import java.util.Objects;
public class Person {
private String name;
private int age;
// ... konstruktor, getter-lər və s.
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
Vacib məqam: Objects.hash varargs istifadə edir və massiv yaradır — nadir, yüksək yüklü yerlərdə əl ilə yazılmış hashCode daha sürətli ola bilər. Əksər tətbiqlər üçün fərq əhəmiyyətsizdir.
3. Objects.compare: müqayisəni komparatora həvalə edirik
Bəzən iki obyekt əvvəlcədən hazırlanmış Comparator ilə müqayisə olunmalıdır. comparator.compare(a, b) birbaşa çağırmaq əvəzinə bunu istifadə edə bilərsiniz:
int result = Objects.compare(a, b, comparator);
Bu metod:
- Obyektlər bərabərdirsə, 0 qaytarır.
- null-ı istənilən non-null obyektdən “kiçik” hesab edir.
- Qalan hallarda məntiqi ötürülmüş komparatora həvalə edir.
import java.util.Comparator;
import java.util.Objects;
class Person {
private String name;
Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class Main {
public static void main(String[] args) {
Person a = new Person("Anna");
Person b = new Person("Boris");
Comparator<Person> byName = Comparator.comparing(Person::getName);
System.out.println(Objects.compare(a, b, byName)); // <0, çünki "Anna" < "Boris"
System.out.println(Objects.compare(a, null, byName)); // >0, çünki a != null
System.out.println(Objects.compare(null, b, byName)); // <0, çünki null < b
System.out.println(Objects.compare(null, null, byName)); // 0
}
}
4. Objects.requireNonNull: “görünməyən” səhvlərə qarşı sığorta
Əgər metod yalnız null olmayan dəyərlər qəbul etməlidirsə, bunu dərhal yoxlayın. Objects.requireNonNull sizin mesajınızla birlikdə NullPointerException atacaq:
public void setName(String name) {
this.name = Objects.requireNonNull(name, "Ad null ola bilməz");
}
5. Nümunə: düzgün equals, hashCode və compareTo reallaşdırılması Objects ilə
import java.util.Objects;
public class Person implements Comparable<Person> {
private String name;
private int age;
public Person(String name, int age) {
this.name = Objects.requireNonNull(name, "Ad null ola bilməz");
this.age = age;
}
public String getName() { return name; }
public int getAge() { return age; }
@Override
public boolean equals(Object o) {
if (this == o) return true; // İstinad üzrə müqayisə
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
// null ilə təhlükəsiz müqayisə
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age); // Yığcam və təhlükəsiz
}
@Override
public int compareTo(Person other) {
// Əvvəl adı, sonra yaşı müqayisə edirik
int cmp = name.compareTo(other.name);
if (cmp != 0) return cmp;
return Integer.compare(age, other.age);
}
}
İndi obyektləri HashSet-də saxlamaq, onları HashMap-də açar kimi istifadə etmək, bərabərlik üzrə müqayisə etmək və siyahıları çeşidləmək (məsələn, Collections.sort vasitəsilə) mümkündür.
6. Real tapşırıqlarda tətbiqi: kodun qısalması və səhvlərin azalması
Nümunə: tətbiqdə istifadəçilər siyahısı
Düzgün equals/hashCode cütlüyü sayəsində kolleksiyalarda axtarış proqnozlaşdırıla bilən olur:
import java.util.ArrayList;
import java.util.List;
List<Person> users = new ArrayList<>();
users.add(new Person("Anna", 25));
users.add(new Person("Boris", 30));
Person search = new Person("Anna", 25);
System.out.println(users.contains(search)); // true
Nümunə: nullable sahələrlə iş
Əgər sinifdə null ola bilən sahələr (məsələn, ata adı) varsa, Objects.equals və Objects.hash istifadə edin:
import java.util.Objects;
public class User {
private String firstName;
private String middleName; // null ola bilər
private String lastName;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(firstName, user.firstName)
&& Objects.equals(middleName, user.middleName)
&& Objects.equals(lastName, user.lastName);
}
@Override
public int hashCode() {
return Objects.hash(firstName, middleName, lastName);
}
}
7. Cədvəl: Objects sinfinin əsas metodları
| Metod | Təyinatı | İstifadə nümunəsi |
|---|---|---|
|
İki obyektin null nəzərə alınmaqla təhlükəsiz müqayisəsi | |
|
Bir neçə sahə üzrə hash-kodun yığcam hesablanması | |
|
Komparator vasitəsilə müqayisə, null üçün təhlükəsiz | |
|
null yoxlaması, NullPointerException atır | |
|
null/non-null yoxlaması (Stream API-də rahatdır) | |
8. Objects sinfinin metodlarından istifadə zamanı tipik səhvlər
Xəta №1: Objects.equals-dən nullable sahələr üçün istifadə etməyi unutmaq. Sahələri birbaşa equals ilə müqayisə etsəniz, NullPointerException ala bilərsiniz. Objects.equals(middleName, other.middleName) istifadə edin.
Xəta №2: hashCode-da bütün sahələr nəzərə alınmayıb. equals-da iştirak edən sahələr hashCode-da da iştirak etməlidir, əks halda HashSet/HashMap-in davranışı qeyri-proqnozlaşdırılan olacaq.
Xəta №3: əl ilə yazılmış hashCode səhvlidir. Əmsalları saxlamaq və null yoxlamalarını etmək asan deyil. Objects.hash bunu sizin yerinizə edir; sərt performans tələbləri yoxdursa, ondan istifadə edin.
Xəta №4: Objects.requireNonNull-dan sinfin müqaviləsinin tələb etdiyi yerdə istifadə etməmək. Sahə null-a icazə vermirsə, bunu konstruktor/setter-də yoxlayın — səhv dərhal üzə çıxacaq, çağırış stekinin “cənəlliklərində” yox.
Xəta №5: Objects.hash-i massivlər üçün istifadə etmək. Massivlər üçün Arrays.hashCode, iç-içə massivlər üçün isə Arrays.deepHashCode lazımdır; məzmunun müqayisəsi üçün uyğun olaraq Arrays.equals/Arrays.deepEquals mövcuddur.
GO TO FULL VERSION