چون Enum ها نوعی کلاس هستند، Reflection نیازی به تعریف یک کلاس صریح java.lang.reflect.Enum ندارد و در واقع چنین کلاسی وجود ندارد. تنها APIهای Reflection اختصاصی به Enum ها شامل Class.isEnum()، Class.getEnumConstants() و Field.isEnumConstant() هستند. بیشتر عملیات Reflective مربوط به Enum ها مشابه هر کلاس یا عضو دیگری هستند. بهعنوان مثال، ثابتهای Enum بهصورت فیلدهای Public Static Final روی Enum پیادهسازی شدهاند.
Reflection سه API اختصاصی برای Enum ارائه میدهد:
Class.isEnum(): مشخص میکند آیا این کلاس نشاندهنده یک Enum Type است یا خیر.Class.getEnumConstants(): لیست ثابتهای Enum را به ترتیب اعلامشده بازیابی میکند.Field.isEnumConstant(): مشخص میکند آیا این فیلد نشاندهنده یک عنصر از یک Enum Type است یا خیر.گاهی لازم است لیث ثابتهای Enum را بهصورت پویا بازیابی کنید. در کد غیرReflective این کار با فراخوانی متود ایستا implicitly declared یعنی values() روی Enum انجام میشود. اگر نمونهای از یک Enum Type در دسترس نباشد، تنها راه دریافت لیست مقادیر ممکن، فراخوانی Class.getEnumConstants() است، چون غیرممکن است یک Enum Type را نمونهسازی (Instantiate) کرد.
فرض کنید Enum زیر را دارید:
public enum Days {
SATURDAY, SUNDAY
}
کد زیر به شما امکان میدهد بهصورت Reflective به عناصر مختلف این Enum دسترسی پیدا کنید:
Class<?> c = Days.class;
IO.println("class: " + c.getName());
IO.println("values: " + Arrays.toString(Days.values()));
IO.println("constants: " + Arrays.toString(c.getEnumConstants()));
خروجی:
class: org.devjava.Days
values: [SATURDAY, SUNDAY]
constants: [SATURDAY, SUNDAY]
همانطور که میبینید، فراخوانی c.getEnumConstants() همان آرایهای را برمیگرداند که Days.values() بازمیگرداند.
با استفاده از Reflection API میتوانید Constructor های Enum Days را پیدا کنید:
Class<Days> c = Days.class;
Constructor<?>[] constructors = c.getDeclaredConstructors();
int length = constructors.length;
IO.println("# constructors = " + length);
IO.println("constructors[0]: " + constructors[0]);
خروجی نشان میدهد Enum Days یک Constructor دارد که Private است و دو پارامتر میگیرد:
String که نام ثابت استint که اندیس این ثابت در آرایه ثابتهای Define شده توسط Enum است# constructors = 1
constructors[0]: private org.devjava.Days(java.lang.String,int)
میتوانید سعی کنید به این Constructor رفرنس بگیرید و آن را برای ایجاد یک ثابت جدید در Enum Days فراخوانی کنید:
Class<Days> c = Days.class;
Constructor<Days> constructor = c.getDeclaredConstructor(String.class, int.class);
IO.println("constructor = " + constructor);
constructor.setAccessible(true);
Days monday = constructor.newInstance("MONDAY", 2);
خروجی و خطای پرتابشده:
constructor = private org.devjava.Days(java.lang.String,int)
Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
at java.base/java.lang.reflect.Constructor.acquireConstructorAccessor(Constructor.java:547)
at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499)
at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486)
at org.devjava.Main.main(ScrapMain.java:42)
در واقع، اضافه کردن ثابتهای جدید به یک Enum در runtime حتی با Reflection API غیرممکن است.
فرض کنید Enum زیر را دارید. از نظر فنی خورشید یک سیاره نیست، اما کسانی که نام روزهای هفته را انتخاب کردند در آن زمان چنین تصوری داشتند:
public enum Days {
SATURDAY("Saturn"), SUNDAY("Sun");
private final String planet;
Days(String planet) {
this.planet = planet;
}
public static Days of(String label) {
return Arrays.stream(values())
.filter(day -> day.planet.equals(label))
.findAny().orElseThrow();
}
public String planet() {
return this.planet;
}
}
این Enum یک Constructor، یک فیلد و یک متود (یک Accessor برای فیلد) دارد. همچنین شامل یک Factory Method برای دریافت یک ثابت از یک لیبل است.
میتوانید تعداد Constructor های این Enum را کشف کنید:
Class<Days> c = Days.class;
Constructor<?>[] constructors = c.getDeclaredConstructors();
int length = constructors.length;
IO.println("# constructors = " + length);
این Enum هنوز فقط یک Constructor دارد: همان Constructorی که تعریف کردید. اما همانطور که میبینید، پارامترهای بیشتری نسبت به چیزی که تعریف کردهاید دارد. کامپایلر نام ثابت و اندیس آن را اضافه کرده است. همچنان غیرممکن است این Constructor را بهصورت Reflective فراخوانی کنید تا ثابتهای بیشتری در runtime ایجاد شوند.
# constructors = 1
constuctor[0]: private org.devjava.Days(java.lang.String,int,java.lang.String)
تمام اعضای دیگر Enum شما بهصورت Reflective قابل دسترسی هستند، درست مانند هر عضو دیگری از هر کلاس معمولی.
Reflection API میتواند به شما دسترسی به ساختار داخلی کلاس Days که کامپایلر برای شما تولید کرده است، بدهد.
بیایید با فیلدها شروع کنیم. برخی از آنها Static هستند، پس برای دریافت مقدارشان باید field.get(null) را فراخوانی کنید، چون یک فیلد Static به هیچ شیئی وابسته نیست. و برخی از آنها آرایه هستند.
Class<Days> c = Days.class;
Field[] fields = c.getDeclaredFields();
for (Field field : fields) {
IO.println(field);
if (Modifier.isStatic(field.getModifiers())) {
field.setAccessible(true);
if (field.getType().isArray()) {
IO.println(" " + Arrays.toString((Object[])field.get(null)));
IO.println(" is enum constant? " + field.isEnumConstant());
} else {
IO.println(" " + field.get(null));
IO.println(" is enum constant? " + field.isEnumConstant());
}
}
}
خروجی چند نکته مهم دارد:
$VALUES با تمام ثابتهای تعریفشده ایجاد میکند. این آرایه توسط فراخوانی Days.values() بازمیگردد.public static final org.paumard.ScrapMain$Days org.paumard.ScrapMain$Days.SATURDAY
SATURDAY
is enum constant? true
public static final org.paumard.ScrapMain$Days org.paumard.ScrapMain$Days.SUNDAY
SUNDAY
is enum constant? true
private static final org.paumard.ScrapMain$Days[] org.paumard.ScrapMain$Days.$VALUES
[SATURDAY, SUNDAY]
is enum constant? false
همچنین میتوانید متدهایی را که کامپایلر برای شما ایجاد کرده است کشف کنید.
Class<Days> c = Days.class;
Method[] methods = c.getDeclaredMethods();
for (Method method : methods) {
IO.println(method);
}
متدهای تولیدشده توسط کامپایلر:
Days.values(): متود کلاسیکی که آرایه ثابتهای تعریفشده را برمیگرداند.Days.valueOf(String): متود کلاسیک دیگری که یک ثابت را از نامش برمیگرداند.Days.$values(): یک متود Private که توسط کامپایلر ایجاد شده است.متود Days.lambda$of$0(String, Days) مربوط به Predicate ای است که در متود of() ایجاد شده است.
public static org.devjava.Days[] org.devjava.Days.values()
public static org.devjava.Days org.devjava.Days.valueOf(java.lang.String)
public static org.devjava.Days org.devjava.Days.of(java.lang.String)
private static org.devjava.Days[] org.devjava.Days.$values()
private static boolean org.devjava.Days.lambda$of$0(java.lang.String,org.devjava.Days)
public java.lang.String org.devjava.Days.planet()
این محتوا کاملا رایگان توسط تیم کدلپر ترجمه شده و در اختیار شما کاربران عزیز قرار گرفته است، هر گونه کپی برداری برای مقاصد غیر رایگان و بدون ذکر منبع، مورد پیگیری قانونی قرار میگیرد.
ترجمه شده از منبع: https://dev.java/learn/