شیء Method به شما امکان دریافت اطلاعات بیشتر درباره متود مربوطه را میدهد: Type و Modifier های آن، Type و نام پارامترها و همچنین فراخوانی آن روی یک شیء خاص با پاس دادن آرگومانهای لازم. این بخش نحوه کشف متدها در یک کلاس را نیز پوشش میدهد.
کلاس Class دو دسته متود برای دسترسی به متدها ارائه میدهد:
| Class API | آرایه متدها؟ | متدهای ارثبریشده؟ | متدهای Private؟ |
|---|---|---|---|
getDeclaredMethod(String, Class...) | خیر | خیر | بله |
getMethod(String, Class...) | خیر | بله | خیر |
getDeclaredMethods() | بله | خیر | بله |
getMethods() | بله | بله | خیر |
فرض کنید کلاس زیر را دارید:
public class User {
private final String name;
public User(String name) {
validate(name);
this.name = name;
}
private void validate(String name) {
Objects.requireNonNull(name);
}
public String getName() {
return name;
}
@Override
public String toString() {
return "User[" + "name=" + name + "]";
}
}
ابتدا متدهای Public کلاس User و تمام Super Class هایش را دریافت کنید:
Class<?> c = User.class;
Method[] methods = c.getMethods();
for (Method method : methods) {
IO.println("method = " + method);
}
خروجی شامل متدهای User (getName() و toString()) و متدهای کلاس Object است:
method = public java.lang.String org.devjava.User.getName()
method = public java.lang.String org.devjava.User.toString()
method = public boolean java.lang.Object.equals(java.lang.Object)
method = public native int java.lang.Object.hashCode()
method = public final native java.lang.Class java.lang.Object.getClass()
method = public final native void java.lang.Object.notify()
method = public final native void java.lang.Object.notifyAll()
method = public final void java.lang.Object.wait(long) throws java.lang.InterruptedException
method = public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
method = public final void java.lang.Object.wait() throws java.lang.InterruptedException
حالا با Class.getDeclaredMethods() فقط متدهای خود کلاس User را دریافت کنید:
Class<?> c = User.class;
Method[] methods = c.getDeclaredMethods();
for (Method method : methods) {
IO.println("method = " + method);
}
فقط متدهای declared شده شامل Private ها نمایش داده میشوند:
method = public java.lang.String org.devjava.User.getName()
method = public java.lang.String org.devjava.User.toString()
method = private void org.devjava.User.validate(java.lang.String)
Type بازگشتی متود مشابه Type فیلد مدلسازی شده است. متود Method.getReturnType() Type را بهصورت Class و Method.getGenericReturnType() Type با اطلاعات Generic برمیگرداند.
بیایید این دو متود را روی Interface List ببینیم.
ابتدا به متود List.get(int) رفرنس بگیرید. این متود عنصر لیست با اندیس پاسدادهشده را برمیگرداند. Type بازگشتی Type پارامتر لیستی است که اعلام کردهاید.
توجه کنید از کلاس int.class برای نشان دادن Type پارامتر int متود List.get(index) استفاده شده. استفاده از Integer.class خطای NoSuchMethodException پرتاب میکرد.
Class<?> listClass = List.class;
Method getWithIndex = listClass.getMethod("get", int.class);
Class<?> returnType = getWithIndex.getReturnType();
IO.println("Return type = " + returnType);
Type genericReturnType = getWithIndex.getGenericReturnType();
IO.println("Generic return type = " + genericReturnType);
خروجی:
Return type = class java.lang.Object
Generic return type = E
همانطور که میبینید، Method.getReturnType() Type خام را برمیگرداند، در حالی که Method.getGenericReturnType() اطلاعات Generic شامل Type پارامتری List را برمیگرداند.
بیایید با متود دیگری تست کنیم: List.of().
Class<?> listClass = List.class;
Method listOf = listClass.getMethod("of", Object.class);
Class<?> returnType = listOf.getReturnType();
IO.println("Return type = " + returnType);
Type genericReturnType = listOf.getGenericReturnType();
IO.println("Generic return type = " + genericReturnType);
خروجی:
Return type = interface java.util.List
Generic return type = java.util.List<E>
همانطور که میبینید، Method.getReturnType() Type خام و Method.getGenericReturnType() اطلاعات Generic شامل Type پارامتری List را برمیگرداند.
کلاس Method به شما دسترسی به پارامترهای یک متود خاص شامل نامهایشان را میدهد.
نام پارامترهای رسمی هر متود یا Constructor را با متود getParameters() میتوانید دریافت کنید. کلاسهای Method و Constructor از کلاس Executable ارث میبرند.
نکته مهم: فایلهای .class بهصورت پیشفرض نام پارامترهای رسمی را ذخیره نمیکنند. برای ذخیره آنها باید با آپشن -parameters کامپایلر javac کد را کامپایل کنید.
کلاس Method به شما دسترسی به Exception های declared شده یک متود میدهد. این Exception ها بهصورت آرایهای از نمونههای Class یا Type بازگردانده میشوند.
بیایید Exception های متود Constructor.newInstance() را کشف کنیم:
Class<?> constructorClass = Constructor.class;
Method newInstance = constructorClass.getMethod("newInstance", Object[].class);
var exceptionTypes = newInstance.getExceptionTypes();
IO.println("exceptionTypes:");
for (var exceptionType : exceptionTypes) {
IO.println(" exceptionType = " + exceptionType);
}
خروجی:
Exception types:
Exception type = class java.lang.InstantiationException
Exception type = class java.lang.IllegalAccessException
Exception type = class java.lang.IllegalArgumentException
Exception type = class java.lang.reflect.InvocationTargetException
همچنین میتوانید همان کد را با الگوی Generic اجرا کنید که آرایهای از اشیاء Type برمیگرداند:
Class<?> constructorClass = Constructor.class;
Method newInstance = constructorClass.getMethod("newInstance", Object[].class);
var exceptionTypes = newInstance.getGenericExceptionTypes();
IO.println("Exception types:");
for (var exceptionType : exceptionTypes) {
IO.println(" Exception type = " + exceptionType);
}
خروجی یکسان است:
Exception types:
Exception type = class java.lang.InstantiationException
Exception type = class java.lang.IllegalAccessException
Exception type = class java.lang.IllegalArgumentException
Exception type = class java.lang.reflect.InvocationTargetException
Modifier متدها مشابه Modifier کلاسها یا فیلدها عمل میکنند. متود Method.getModifiers() عددی int برمیگرداند که هر بیت نشاندهنده یک Modifier است.
Class<?> stringClass = String.class;
Method join = stringClass.getMethod("join", CharSequence.class, Iterable.class);
int modifiers = join.getModifiers();
IO.println("is static? " + Modifier.isStatic(modifiers));
IO.println("is public? " + Modifier.isPublic(modifiers));
is static? true
is public? true
Access Flag ها نیز قابل استفاده هستند:
Class<?> stringClass = String.class;
Method join = stringClass.getMethod("join", CharSequence.class, Iterable.class);
Set<AccessFlag> accessFlags = join.accessFlags();
IO.println("Access flags: " + accessFlags);
Access flags: [PUBLIC, STATIC]
تا اینجا آموختید که یک متود توسط نمونهای از کلاس Method مدلسازی میشود و میتوانید از این شیء برای دریافت اطلاعات استفاده کنید. اما همچنین میتوانید این متود را با پاس دادن آرگومانها فراخوانی کنید.
یک متود با متود invoke() فراخوانی میشود که دو پارامتر دارد:
فرض کنید میخواهید طول یک رشته را با Reflection API دریافت کنید:
String hello = "Hello";
Class<?> stringClass = String.class;
Method lengthMethod = stringClass.getMethod("length");
int length = (int)lengthMethod.invoke(hello);
IO.println("length = " + length);
توجه کنید invoke() نوع Object برمیگرداند و باید Cast شود.
length = 5
آرگومانها بهعنوان آرگومانهای اضافی به invoke() پاس داده میشوند. در مثال زیر متود substring() که دو عدد صحیح میگیرد فراخوانی میشود:
String hello = "Hello world!";
Class<?> stringClass = String.class;
Method substringMethod = stringClass.getMethod("substring", int.class, int.class);
String substring = (String)substringMethod.invoke(hello, 6, 11);
IO.println("substring = " + substring);
substring = world
در مواردی متود فراخوانیشده از طریق Reflection API ممکن است Exception پرتاب کند. Reflection API این Exception ها را در یک InvocationTargetException بستهبندی میکند.
فرض کنید میخواهید فایلی را برای نوشتن باز کنید اما فایل وجود ندارد:
Path doesNotExist = Path.of("/does/not/exist");
Class<?> filesClass = Files.class;
Method newBufferedWriterMethod =
filesClass.getMethod("newBufferedWriter", Path.class, OpenOption[].class);
try (Writer writer = (Writer)newBufferedWriterMethod.invoke(null, doesNotExist, new OpenOption[0]);) {
writer.write("Hello");
} catch (InvocationTargetException ite) {
Throwable targetException = ite.getTargetException();
IO.println("ite.getTargetException() = " + targetException);
IO.println("Stack trace:");
targetException.printStackTrace();
} catch (IOException e) {
throw new RuntimeException(e);
}
توجه کنید Files.newBufferedWriter() یک متود Static است، بنابراین آرگومان اول invoke() برابر null است.
خروجی:
ite.getTargetException() = java.nio.file.NoSuchFileException: \does\not\exist
Stack trace:
java.nio.file.NoSuchFileException: \does\not\exist
// this part of the stack trace depends on your file system
at java.base/java.nio.file.spi.FileSystemProvider.newOutputStream(FileSystemProvider.java:484)
at java.base/java.nio.file.Files.newOutputStream(Files.java:228)
at java.base/java.nio.file.Files.newBufferedWriter(Files.java:3007)
at java.base/java.nio.file.Files.newBufferedWriter(Files.java:3055)
at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104)
at java.base/java.lang.reflect.Method.invoke(Method.java:578)
at org.devjava.Main.main(Main.java:48)
فراخوانی یک متود Private از خارج کلاس، IllegalAccessException پرتاب میکند.
میتوانید ابتدا method.setAccessible(true) را فراخوانی کنید تا دسترسی فراهم شود. همانند فیلدها، محدودیتهایی برای متدهایی که میتوانید قابل دسترسی کنید وجود دارد.
فراخوانی
setAccessible(true)روی یک متود آن را Public نمیکند. فقط بررسیهای Access Control را غیرفعال میکند.
به یاد داشته باشید کشف یک متود به نحوه کامپایل آن بستگی دارد. Type پارامترهای یک متود در زمان کامپایل Erased میشوند و با Reflection API قابل استفاده نیستند.
فرض کنید میخواهید متود add() یک شیء List را دسترسی کنید:
List<String> strings = new ArrayList<>();
Class<?> stringsClass = strings.getClass();
Method addString = stringsClass.getMethod("add", String.class);
چون Type واقعی پارامتر E است (که در این مثال String میشود)، Erased شده و در واقع Object است. بنابراین خطای NoSuchMethodException دریافت میکنید.
کد صحیح:
List<String> strings = new ArrayList<>();
Class<?> stringsClass = strings.getClass();
Method addString = stringsClass.getMethod("add", Object.class);
با اجرای کد اول، خطای زیر دریافت میکنید:
Exception in thread "main" java.lang.NoSuchMethodException: java.util.ArrayList.add(java.lang.String)
at java.base/java.lang.Class.getMethod(Class.java:2277)
at org.devjava.Main.main(Main.java:47)
سپس میتوانید این متود را با پاس دادن یک رشته فراخوانی کنید. اما با این کار Type Safety فراخوانی مستقیم متود را از دست میدهید.
این محتوا کاملا رایگان توسط تیم کدلپر ترجمه شده و در اختیار شما کاربران عزیز قرار گرفته است، هر گونه کپی برداری برای مقاصد غیر رایگان و بدون ذکر منبع، مورد پیگیری قانونی قرار میگیرد.
ترجمه شده از منبع: https://dev.java/learn/