فئة السجلات في جافا 14

تقدم Java 14 طريقة جديدة لإنشاء الفئات تسمى Records. في هذا البرنامج التعليمي، سنتعلم:

  • لماذا نحتاج إلى Java Records
  • كيفية إنشاء Records واستخدامها
  • تجاوز وتوسيع فئات Records

القراءة الموصى بها: ميزات Java 14

لماذا نحتاج إلى Java Records؟

كانت إحدى الشكاوى الشائعة حول Java هي ضجيجها الزائد. إذا كنت بحاجة إلى إنشاء فئة POJO بسيطة، فإنه يتطلب الشيفرة الروتينية التالية.

  • حقول خاصة
  • طرق الحصول والتعيين (Getter وSetter)
  • بناة (Constructors)
  • طرق hashCode()، equals()، وtoString()

هذا الضجيج هو واحد من الأسباب وراء اهتمام كبير في Kotlin و Project Lombok.

في الواقع، كانت إحباطات كتابة هذه الطرق العامة في كل مرة تؤدي إلى الاختصارات لإنشائها في بيئات تطوير Java مثل Eclipse و IntelliJ IDEA.

فيما يلي لقطة شاشة تظهر خيار بيئة تطوير Eclipse لإنشاء الطرق الاحتفالية لفئة.

Eclipse Shortcuts to Generate Ceremonial Methods

تهدف Java Records إلى إزالة هذا الضجيج عن طريق توفير هيكل مدمج لإنشاء فئات POJO.

كيفية إنشاء سجلات Java

سجلات Java هي ميزة معاينة تم تطويرها في إطار JEP 359. لذلك، تحتاج إلى شيئين لإنشاء سجلات في مشاريع Java الخاصة بك.

  1. يجب تثبيت JDK 14. إذا كنت تستخدم بيئة تطوير متكاملة، فيجب أن توفر دعمًا لـ Java 14 أيضًا. توفر Eclipse و IntelliJ بالفعل دعمًا لـ Java 14، لذا نحن بخير هنا.
  2. تمكين ميزة المعاينة: بشكل افتراضي، تكون ميزات المعاينة معطلة. يمكنك تمكينها في Eclipse من إعدادات مترجم Java للمشروع.
Java 14 Enable Preview Feature In Eclipse

يمكنك تمكين ميزات معاينة Java 14 في سطر الأوامر باستخدام الخيار --enable-preview -source 14.

لنفترض أنني أريد إنشاء فئة نموذج موظف. ستبدو بشكل ما مثل الشيفرة التالية.

package com.journaldev.java14;

import java.util.Map;

public class Employee {

	private int id;
	private String name;
	private long salary;
	private Map<String, String> addresses;

	public Employee(int id, String name, long salary, Map<String, String> addresses) {
		super();
		this.id = id;
		this.name = name;
		this.salary = salary;
		this.addresses = addresses;
	}

	public int getId() {
		return id;
	}

	public String getName() {
		return name;
	}

	public long getSalary() {
		return salary;
	}

	public Map<String, String> getAddresses() {
		return addresses;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((addresses == null) ? 0 : addresses.hashCode());
		result = prime * result + id;
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		result = prime * result + (int) (salary ^ (salary >>> 32));
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Employee other = (Employee) obj;
		if (addresses == null) {
			if (other.addresses != null)
				return false;
		} else if (!addresses.equals(other.addresses))
			return false;
		if (id != other.id)
			return false;
		if (name == null) {
			if (other.name != null)
				return false;
		} else if (!name.equals(other.name))
			return false;
		if (salary != other.salary)
			return false;
		return true;
	}

	@Override
	public String toString() {
		return "Employee [id=" + id + ", name=" + name + ", salary=" + salary + ", addresses=" + addresses + "]";
	}

}

أوووه، هذا أكثر من 70 سطرًا من الشيفرة المولدة تلقائيًا. الآن دعونا نرى كيفية إنشاء فئة سجل موظف، التي توفر في الأساس نفس الميزات.

package com.journaldev.java14;

import java.util.Map;

public record EmpRecord(int id, String name, long salary, Map<String, String> addresses) {
}

واو، لا يمكن أن يكون أقصر من ذلك. أنا بالفعل أحب فئات السجلات.

الآن، دعونا نستخدم الأمر javap لمعرفة ما يحدث خلف الكواليس عند ترجمة سجل.

# javac --enable-preview -source 14 EmpRecord.java
Note: EmpRecord.java uses preview language features.
Note: Recompile with -Xlint:preview for details.

# javap EmpRecord      
Compiled from "EmpRecord.java"
public final class EmpRecord extends java.lang.Record {
  public EmpRecord(int, java.lang.String, long, java.util.Map<java.lang.String, java.lang.String>);
  public java.lang.String toString();
  public final int hashCode();
  public final boolean equals(java.lang.Object);
  public int id();
  public java.lang.String name();
  public long salary();
  public java.util.Map<java.lang.String, java.lang.String> addresses();
}
# 
Java Record Class Details

# javac تمكينمعاينة المصدر 14 EmpRecord.جافا

# javap EmpRecord

إذا كنت ترغب في المزيد من التفاصيل الداخلية، قم بتشغيل الأمر javap مع خيار -v.

  1. A Record class is final, so we can’t extend it.
  2. # javap -v EmpRecord
  3. نقاط مهمة حول فئات السجلات
  4. توسيع فئات السجلات بشكل ضمني java.lang.Record فئة.
  5. A single constructor is created with all the fields specified in the record definition.
  6. جميع الحقول المحددة في إعلان السجل هي نهائية.
  7. حقول السجل غير قابلة للتغيير “سطحيًا” وتعتمد على النوع. على سبيل المثال، يمكننا تغيير حقل العناوين عن طريق الوصول إليه ومن ثم إجراء التحديثات عليه.

توفر فئة السجل تلقائيًا طرق وصول للحقول. اسم الطريقة هو نفس اسم الحقل، وليس مثل الطرق العامة والتقليدية للحصول على القيم.

توفر فئة السجل طرق تنفيذ hashCode()، equals()، و toString() أيضًا.

package com.journaldev.java14;

public class RecordTest {

	public static void main(String[] args) {
		
		EmpRecord empRecord1 = new EmpRecord(10, "Pankaj", 10000, null);
		EmpRecord empRecord2 = new EmpRecord(10, "Pankaj", 10000, null);

		استخدام السجلات في برنامج Java
		System.out.println(empRecord1);
		
		لنلق نظرة على مثال بسيط عن استخدام فئة EmpRecord لدينا.
		System.out.println("Name: "+empRecord1.name()); 
		System.out.println("ID: "+empRecord1.id());
		
		// toString()
		System.out.println(empRecord1.equals(empRecord2));
		
		// الوصول إلى الحقول
		System.out.println(empRecord1 == empRecord2);		
	}
}

// equals()

EmpRecord[id=10, name=Pankaj, salary=10000, addresses=null]
Name: Pankaj
ID: 10
true
false

// hashCode()

الإخراج:

يعمل كائن السجل بنفس الطريقة كأي فئة نموذجية، كائن بيانات، إلخ.

public record EmpRecord(int id, String name, long salary, Map<String, String> addresses) {
	
	public EmpRecord {
		if (id < 0)
			throw new IllegalArgumentException("employee id can't be negative");

		if (salary < 0)
			throw new IllegalArgumentException("employee salary can't be negative");
	}

}

توسيع بناء السجل

EmpRecord empRecord1 = new EmpRecord(-10, "Pankaj", 10000, null);

أحيانًا، نرغب في إضافة بعض التحققات أو تسجيلات في البناء الخاص بنا. على سبيل المثال، لا ينبغي أن يكون معرف الموظف والراتب سالبين. البناء الافتراضي لن يحتوي على هذا التحقق. يمكننا إنشاء بناء مضغوط في فئة السجل. سيتم وضع كود هذا البناء في بداية البناء الذي تم إنشاؤه تلقائيًا.

Exception in thread "main" java.lang.IllegalArgumentException: employee id can't be negative
	at com.journaldev.java14.EmpRecord.<init>(EmpRecord.java:9)

إذا قمنا بإنشاء EmpRecord مثل الكود التالي:

ستحدث استثناء تشغيلي عند تنفيذ:

public record EmpRecord(int id, String name, long salary, Map<String, String> addresses) {

	public int getAddressCount() {
		if (this.addresses != null)
			return this.addresses().size();
		else
			return 0;
	}
}

هل يمكن لفئات التسجيلات أن تحتوي على طرق؟

نعم، يمكننا إنشاء طريقة في التسجيلات.

ولكن، التسجيلات مخصصة لحمل البيانات. يجب تجنب وجود طرق فائدية في فئة التسجيل. على سبيل المثال، يمكن إنشاء الطريقة أعلاه في فئة فائدية.

إذا كنت تعتقد أن وجود طريقة ضروري لفئتك التسجيل، فكّر بعناية هل تحتاج حقًا إلى فئة تسجيل؟

Source:
https://www.digitalocean.com/community/tutorials/java-records-class