Spec-Zone .ru
спецификации, руководства, описания, API
|
Этот раздел содержит примеры трудных разработчиков, мог бы встретиться при использовании отражения, чтобы определить местоположение, вызвать, или получить информацию о методах.
пример иллюстрирует то, что происходит, когда стирание типа не учитывается кодом, который ищет определенный метод в class.MethodTrouble
import java.lang.reflect.Method; public class MethodTrouble<T> { public void lookup(T t) {} public void find(Integer i) {} public static void main(String... args) { try { String mName = args[0]; Class cArg = Class.forName(args[1]); Class<?> c = (new MethodTrouble<Integer>()).getClass(); Method m = c.getMethod(mName, cArg); System.out.format("Found:%n %s%n", m.toGenericString()); // production code should handle these exceptions more gracefully } catch (NoSuchMethodException x) { x.printStackTrace(); } catch (ClassNotFoundException x) { x.printStackTrace(); } } }
$ java MethodTrouble lookup java.lang.Integer java.lang.NoSuchMethodException: MethodTrouble.lookup(java.lang.Integer) at java.lang.Class.getMethod(Class.java:1605) at MethodTrouble.main(MethodTrouble.java:12)
$ java MethodTrouble lookup java.lang.Object Found: public void MethodTrouble.lookup(T)
Когда метод будет объявлен с универсальным типом параметра, компилятор заменит универсальный тип своей верхней границей, в этом случае, верхней границей T
Object
. Таким образом, когда код ищет lookup(Integer)
, никакой метод не находится, несмотря на то, что экземпляр MethodTrouble
создавался следующим образом:
Class<?> c = (new MethodTrouble<Integer>()).getClass();
Поиск lookup(Object)
успешно выполняется как ожидалось.
$ java MethodTrouble find java.lang.Integer Found: public void MethodTrouble.find(java.lang.Integer) $ java MethodTrouble find java.lang.Object java.lang.NoSuchMethodException: MethodTrouble.find(java.lang.Object) at java.lang.Class.getMethod(Class.java:1605) at MethodTrouble.main(MethodTrouble.java:12)
В этом случае, find()
не имеет никаких универсальных параметров, таким образом, типы параметра, разыскиваемые getMethod()
IllegalAccessException
private
или иначе недоступный метод.
пример показывает типичную трассировку стека, которая следует из попытки вызвать закрытый метод в другом class.MethodTroubleAgain
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; class AnotherClass { private void m() {} } public class MethodTroubleAgain { public static void main(String... args) { AnotherClass ac = new AnotherClass(); try { Class<?> c = ac.getClass(); Method m = c.getDeclaredMethod("m"); // m.setAccessible(true); // solution Object o = m.invoke(ac); // IllegalAccessException // production code should handle these exceptions more gracefully } catch (NoSuchMethodException x) { x.printStackTrace(); } catch (InvocationTargetException x) { x.printStackTrace(); } catch (IllegalAccessException x) { x.printStackTrace(); } } }
Трассировка стека для выданного исключения следует.
$ java MethodTroubleAgain java.lang.IllegalAccessException: Class MethodTroubleAgain can not access a member of class AnotherClass with modifiers "private" at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65) at java.lang.reflect.Method.invoke(Method.java:588) at MethodTroubleAgain.main(MethodTroubleAgain.java:15)
private
методы в отдельном class и открытые методы в отдельном частном class.) Однако, Method
как объявляют, расширяется AccessibleObject
который обеспечивает возможность подавить эту проверку через AccessibleObject.setAccessible()
Method.invoke()
пример показывает различные пути в который MethodTroubleToo
Method.invoke()
import java.lang.reflect.Method; public class MethodTroubleToo { public void ping() { System.out.format("PONG!%n"); } public static void main(String... args) { try { MethodTroubleToo mtt = new MethodTroubleToo(); Method m = MethodTroubleToo.class.getMethod("ping"); switch(Integer.parseInt(args[0])) { case 0: m.invoke(mtt); // works break; case 1: m.invoke(mtt, null); // works (expect compiler warning) break; case 2: Object arg2 = null; m.invoke(mtt, arg2); // IllegalArgumentException break; case 3: m.invoke(mtt, new Object[0]); // works break; case 4: Object arg4 = new Object[0]; m.invoke(mtt, arg4); // IllegalArgumentException break; default: System.out.format("Test not found%n"); } // production code should handle these exceptions more gracefully } catch (Exception x) { x.printStackTrace(); } } }
$ java MethodTroubleToo 0 PONG!
Начиная со всех параметров Method.invoke()
$ java MethodTroubleToo 1 PONG!
Код в этом случае генерирует это предупреждение компилятора потому что null
неоднозначно.
$ javac MethodTroubleToo.java MethodTroubleToo.java:16: warning: non-varargs call of varargs method with inexact argument type for last parameter; m.invoke(mtt, null); // works (expect compiler warning) ^ cast to Object for a varargs call cast to Object[] for a non-varargs call and to suppress this warning 1 warning
Не возможно определить ли null
представляет пустой массив параметров или первого параметра null
.
$ java MethodTroubleToo 2 java.lang.IllegalArgumentException: wrong number of arguments at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at MethodTroubleToo.main(MethodTroubleToo.java:21)
Это перестало работать несмотря на то, что параметр null
, потому что тип является a Object
ping()
не ожидает параметров вообще.
$ java MethodTroubleToo 3 PONG!
Это работает потому что new Object[0]
создает пустой массив, и к varargs методу, это эквивалентно не передаче любого из дополнительных параметров.
$ java MethodTroubleToo 4 java.lang.IllegalArgumentException: wrong number of arguments at sun.reflect.NativeMethodAccessorImpl.invoke0 (Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:39) at sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:25) at java.lang.reflect.Method.invoke(Method.java:597) at MethodTroubleToo.main(MethodTroubleToo.java:28)
В отличие от предыдущего примера, если пустой массив сохранен в Object
Object
ping()
не ожидает параметр.
foo(Object... o)
объявляется компилятор поместит все параметры, к которым передают foo()
в массиве типа Object
foo()
то же самое, как будто оно было объявлено foo(Object[] o)
. Понимание этого может помочь избежать типов проблем, иллюстрированных выше. InvocationTargetException
пример показывает, как получить исходное исключение, выданное вызванным методом.MethodTroubleReturns
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class MethodTroubleReturns { private void drinkMe(int liters) { if (liters < 0) throw new IllegalArgumentException("I can't drink a negative amount of liquid"); } public static void main(String... args) { try { MethodTroubleReturns mtr = new MethodTroubleReturns(); Class<?> c = mtr.getClass(); Method m = c.getDeclaredMethod("drinkMe", int.class); m.invoke(mtr, -1); // production code should handle these exceptions more gracefully } catch (InvocationTargetException x) { Throwable cause = x.getCause(); System.err.format("drinkMe() failed: %s%n", cause.getMessage()); } catch (Exception x) { x.printStackTrace(); } } }
$ java MethodTroubleReturns drinkMe() failed: I can't drink a negative amount of liquid
InvocationTargetException
бросается, метод был вызван. Диагноз проблемы был бы тем же самым, как будто метод вызвали непосредственно и выдал исключение, которое получается getCause()