Record ها نوع خاصی از کلاس هستند که تغییرناپذیری (Non-Modifiability) را بهشدت اعمال میکنند. Record ها میتوانند هر تعداد Record Component داشته باشند. هر دو عامل بر Reflection API تأثیر میگذارند.
وقتی Record ها در Java SE 16 معرفی شدند، چندین متود و کلاس جدید به Reflection API اضافه شد. یکی از آنها متود Class.isRecord() روی کلاس Class برای بررسی اینکه آیا یک کلاس دادهشده Record Class است یا خیر.
فرض کنید Record زیر را دارید:
public record Point(int x, int y) {
public Point() {
this(0, 0);
}
}
بیایید این متود ساده را در عمل ببینیم:
Class<?> c1 = String.class;
Class<?> c2 = Point.class;
IO.println("Is " + c1.getSimpleName() + " a record class? " + c1.isRecord());
IO.println("Is " + c2.getSimpleName() + " a record class? " + c2.isRecord());
خروجی:
Is String a record class? false
Is Point a record class? true
Record ها یک عنصر جدید برای زبان جاوا تعریف میکنند: Record Component. یک Record Component یکی از عناصری است که همراه با اعلام یک Record تعریف میشود. Reflection API نیز از این مفهوم پشتیبانی میکند.
Reflection API یک کلاس جدید دارد: RecordComponent که یک Component از یک Record را مدلسازی میکند. این کلاس متدهای متعددی دارد:
| Component API | توضیحات |
|---|---|
getDeclaringRecord() | کلاسی که این Component را اعلام کرده برمیگرداند. |
getAccessor() | متودی که Accessor این Component را مدلسازی میکند برمیگرداند. |
getName() | نام این Component را برمیگرداند. |
getType() | Type این Component را بهصورت یک شیء Class برمیگرداند. |
getGenericType() | Generic Type این Component را بهصورت یک شیء Type برمیگرداند. |
getGenericSignature() | امضای Generic Type این Component را بهصورت یک رشته برمیگرداند. |
Component های یک Record را میتوانید در یک آرایه از نوع RecordComponent[] دریافت کنید.
بیایید این متدها را در عمل ببینیم:
Class<?> c = Point.class;
RecordComponent[] components = c.getRecordComponents();
Class<?> declaring = components[0].getDeclaringRecord();
IO.println("Declaring record: " + declaring);
String name = components[0].getName();
IO.println("name = " + name);
Method accessor = components[0].getAccessor();
IO.println("accessor = " + accessor);
Class<?> type = components[0].getType();
IO.println("type = " + type);
Type genericType = components[0].getGenericType();
IO.println("genericType = " + genericType);
String genericSignature = components[0].getGenericSignature();
IO.println("genericSignature = " + genericSignature);
خروجی:
Declaring record: class org.devjava.Point
name = x
accessor = public int org.devjava.Point.x()
type = int
genericType = int
genericSignature = null
توجه کنید که هیچ متودی روی کلاس RecordComponent برای دریافت رفرنس به شیء Field مدلسازیکننده فیلد این Component وجود ندارد.
با این حال میتوانید با استفاده از Class.getDeclaredFields() به فیلدهای Record دسترسی پیدا کنید. اما نمیتوانید فیلدهای یک Record را تغییر دهید، حتی با فراخوانی Field.setAccessible(true). این رفتار خاص Record هاست.
فرض کنید Record زیر را دارید:
public record Point(int x, int y) {}
این Record دو فیلد Final Instance با نامهای x و y دارد که با Reflection API قابل دسترسی هستند:
Class<?> c = Point.class;
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
IO.println("field = " + field);
}
field = private final int org.devjava.Point.x
field = private final int org.devjava.Point.y
میتوانید از این فیلدها برای خواندن مقادیرشان استفاده کنید:
Point p = new Point(1, 2);
Field xField = p.getClass().getDeclaredField("x");
xField.setAccessible(true);
int x = (int)xField.get(p);
IO.println("x = " + x);
توجه کنید فیلد x Private است و به همین دلیل نیاز به فراخوانی xField.setAccessible(true) دارید.
x = 1
همچنین میتوانستید از xField.getInt(p) استفاده کنید تا از Auto-Unboxing جلوگیری شود.
اما نمیتوانید مقدار x را تغییر دهید. اگر کد زیر را اجرا کنید:
Point p = new Point(1, 2);
Field xField = p.getClass().getDeclaredField("x");
xField.setAccessible(true);
xField.setInt(p, 0);
IO.println("p = " + p);
خطای IllegalAccessException دریافت خواهید کرد:
Exception in thread "main" java.lang.IllegalAccessException: Can not set final int field org.devjava.Point.x to (int)0
at java.base/jdk.internal.reflect.FieldAccessorImpl.throwFinalFieldIllegalAccessException(FieldAccessorImpl.java:137)
at java.base/jdk.internal.reflect.FieldAccessorImpl.throwFinalFieldIllegalAccessException(FieldAccessorImpl.java:161)
at java.base/jdk.internal.reflect.MethodHandleIntegerFieldAccessorImpl.setInt(MethodHandleIntegerFieldAccessorImpl.java:160)
at java.base/java.lang.reflect.Field.setInt(Field.java:1031)
at org.devjava.Main.main(Main.java:25)
این نشان میدهد فیلدهای Record غیرقابل تغییر هستند حتی با Reflection.
این محتوا کاملا رایگان توسط تیم کدلپر ترجمه شده و در اختیار شما کاربران عزیز قرار گرفته است، هر گونه کپی برداری برای مقاصد غیر رایگان و بدون ذکر منبع، مورد پیگیری قانونی قرار میگیرد.
ترجمه شده از منبع: https://dev.java/learn/