Spec-Zone .ru
спецификации, руководства, описания, API
След: API Reflection
Урок: Элементы
Раздел: Методы
Поиск и устранение неисправностей
Домашняя страница > API Reflection > Элементы

Поиск и устранение неисправностей

Этот раздел содержит примеры трудных разработчиков, мог бы встретиться при использовании отражения, чтобы определить местоположение, вызвать, или получить информацию о методах.

NoSuchMethodException, Должный Вводить Стирание

MethodTrouble пример иллюстрирует то, что происходит, когда стирание типа не учитывается кодом, который ищет определенный метод в class.


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, Вызывая Метод

IllegalAccessException бросается, если попытка предпринимается, чтобы вызвать a private или иначе недоступный метод.

MethodTroubleAgain пример показывает типичную трассировку стека, которая следует из попытки вызвать закрытый метод в другом class.


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(). Если это успешно выполнится, то последующие вызовы этого объекта метода не перестанут работать из-за этой проблемы.

IllegalArgumentException от Method.invoke ()

Method.invoke() был retrofitted, чтобы быть методом переменной арности. Это - огромное удобство, однако оно может привести к неожиданному поведению. 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. Это перестало работать по той же самой причине что случай 2 сбоя, ping() не ожидает параметр.


Подсказка: Когда метод foo(Object... o) объявляется компилятор поместит все параметры, к которым передают foo() в массиве типа Object. Реализация foo() то же самое, как будто оно было объявлено foo(Object[] o). Понимание этого может помочь избежать типов проблем, иллюстрированных выше.

InvocationTargetException, когда Вызванные Сбои Метода

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(). Это исключение не указывает на проблему с отражательным пакетом или его использованием.

Проблемы с примерами? Попытайтесь Компилировать и Выполнить Примеры: FAQ.
Жалобы? Поздравление? Предложения? Дайте нам свою обратную связь.

Предыдущая страница: Вызов Методов
Следующая страница: Конструкторы