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

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

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

InstantiationException из-за Недостающего Конструктора Нулевого параметра

ConstructorTrouble пример иллюстрирует то, что происходит, когда код пытается создать новый экземпляр использования class Class.newInstance() и нет никакого доступного конструктора нулевого параметра:


public class ConstructorTrouble {
    private ConstructorTrouble(int i) {}

    public static void main(String... args){
	try {
	    Class<?> c = Class.forName("ConstructorTrouble");
	    Object o = c.newInstance();  // InstantiationException

        // production code should handle these exceptions more gracefully
	} catch (ClassNotFoundException x) {
	    x.printStackTrace();
	} catch (InstantiationException x) {
	    x.printStackTrace();
	} catch (IllegalAccessException x) {
	    x.printStackTrace();
	}
    }
}
$ java ConstructorTrouble
java.lang.InstantiationException: ConstructorTrouble
        at java.lang.Class.newInstance0(Class.java:340)
        at java.lang.Class.newInstance(Class.java:308)
        at ConstructorTrouble.main(ConstructorTrouble.java:7)

Подсказка: Там много различных причин InstantiationException может произойти. В этом случае проблема состоит в том что присутствие конструктора с int параметр препятствует тому, чтобы компилятор генерировал значение по умолчанию (или нулевой параметр) конструктор и в коде нет никакого явного конструктора нулевого параметра. Помните это Class.newInstance() ведет себя очень как new ключевое слово и перестанет работать всякий раз, когда new перестал бы работать.

Class.newInstance () Выдает Неожиданное Исключение

ConstructorTroubleToo пример показывает неразрешимую проблему в Class.newInstance(). А именно, это распространяет любое исключение — проверенный или непроверенный — брошенный конструктором.


import java.lang.reflect.InvocationTargetException;
import static java.lang.System.err;

public class ConstructorTroubleToo {
    public ConstructorTroubleToo() {
 	throw new RuntimeException("exception in constructor");
    }

    public static void main(String... args) {
	try {
	    Class<?> c = Class.forName("ConstructorTroubleToo");
	    // Method propagetes any exception thrown by the constructor
	    // (including checked exceptions).
	    if (args.length > 0 && args[0].equals("class")) {
		Object o = c.newInstance();
	    } else {
		Object o = c.getConstructor().newInstance();
	    }

        // production code should handle these exceptions more gracefully
	} catch (ClassNotFoundException x) {
	    x.printStackTrace();
	} catch (InstantiationException x) {
	    x.printStackTrace();
	} catch (IllegalAccessException x) {
	    x.printStackTrace();
	} catch (NoSuchMethodException x) {
	    x.printStackTrace();
	} catch (InvocationTargetException x) {
	    x.printStackTrace();
	    err.format("%n%nCaught exception: %s%n", x.getCause());
	}
    }
}
$ java ConstructorTroubleToo class
Exception in thread "main" java.lang.RuntimeException: exception in constructor
        at ConstructorTroubleToo.<init>(ConstructorTroubleToo.java:6)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance
          (NativeConstructorAccessorImpl.java:39)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance
          (DelegatingConstructorAccessorImpl.java:27)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
        at java.lang.Class.newInstance0(Class.java:355)
        at java.lang.Class.newInstance(Class.java:308)
        at ConstructorTroubleToo.main(ConstructorTroubleToo.java:15)

Эта ситуация уникальна для отражения. Обычно, невозможно записать код, который игнорирует проверенное исключение, потому что это не скомпилировало бы. Возможно обернуть любое исключение, выданное конструктором при использовании Constructor.newInstance() вместо Class.newInstance().

$ java ConstructorTroubleToo
java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance
          (NativeConstructorAccessorImpl.java:39)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance
          (DelegatingConstructorAccessorImpl.java:27)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
        at ConstructorTroubleToo.main(ConstructorTroubleToo.java:17)
Caused by: java.lang.RuntimeException: exception in constructor
        at ConstructorTroubleToo.<init>(ConstructorTroubleToo.java:6)
        ... 5 more


Caught exception: java.lang.RuntimeException: exception in constructor

Если InvocationTargetException бросается, метод был вызван. Диагноз проблемы был бы тем же самым, как будто конструктора вызвали непосредственно и выдал исключение, которое получается InvocationTargetException.getCause(). Это исключение не указывает на проблему с отражательным пакетом или его использованием.


Подсказка: предпочтительно использовать Constructor.newInstance() Class.newInstance() потому что прежний API разрешает исследование и обработку произвольных исключений, выданных конструкторами.

Определение местоположения задач или Вызов Корректного Конструктора

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


import java.lang.reflect.InvocationTargetException;
import static java.lang.System.out;

public class ConstructorTroubleAgain {
    public ConstructorTroubleAgain() {}

    public ConstructorTroubleAgain(Integer i) {}

    public ConstructorTroubleAgain(Object o) {
	out.format("Constructor passed Object%n");
    }

    public ConstructorTroubleAgain(String s) {
	out.format("Constructor passed String%n");
    }

    public static void main(String... args){
	String argType = (args.length == 0 ? "" : args[0]);
	try {
	    Class<?> c = Class.forName("ConstructorTroubleAgain");
	    if ("".equals(argType)) {
		// IllegalArgumentException: wrong number of arguments
		Object o = c.getConstructor().newInstance("foo");
	    } else if ("int".equals(argType)) {
		// NoSuchMethodException - looking for int, have Integer
		Object o = c.getConstructor(int.class);
	    } else if ("Object".equals(argType)) {
		// newInstance() does not perform method resolution
		Object o = c.getConstructor(Object.class).newInstance("foo");
	    } else {
		assert false;
	    }

        // production code should handle these exceptions more gracefully
	} catch (ClassNotFoundException x) {
	    x.printStackTrace();
	} catch (NoSuchMethodException x) {
	    x.printStackTrace();
	} catch (InvocationTargetException x) {
	    x.printStackTrace();
	} catch (InstantiationException x) {
	    x.printStackTrace();
	} catch (IllegalAccessException x) {
	    x.printStackTrace();
	}
    }
}
$ java ConstructorTroubleAgain
Exception in thread "main" java.lang.IllegalArgumentException: wrong number of
  arguments
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance
          (NativeConstructorAccessorImpl.java:39)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance
          (DelegatingConstructorAccessorImpl.java:27)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
        at ConstructorTroubleAgain.main(ConstructorTroubleAgain.java:23)

IllegalArgumentException бросается, потому что конструктора нулевого параметра требовали, и попытка была предпринята, чтобы передать параметр. То же самое исключение было бы выдано, если бы конструктора передали параметр неправильного типа.

$ java ConstructorTroubleAgain int
java.lang.NoSuchMethodException: ConstructorTroubleAgain.<init>(int)
        at java.lang.Class.getConstructor0(Class.java:2706)
        at java.lang.Class.getConstructor(Class.java:1657)
        at ConstructorTroubleAgain.main(ConstructorTroubleAgain.java:26)

Это исключение может произойти, если разработчик по ошибке полагает, что отражение будет автополе или распаковывать типы. Упаковка (преобразование примитива к ссылочному типу) происходит только во время компиляции. Нет никакой возможности в отражении для этой работы, чтобы произойти, таким образом, определенный тип должен использоваться, определяя местоположение конструктора.

$ java ConstructorTroubleAgain Object
Constructor passed Object

Здесь, можно было бы ожидать что конструктор, берущий a String параметр был бы вызван с тех пор newInstance() был вызван с более определенным String ввести. Однако это слишком поздно! Конструктор, который был найден, уже является конструктором с Object параметр. newInstance() не предпринимает попытки сделать разрешение метода; это просто работает на существующем объекте конструктора.


Подсказка: важное различие между new и Constructor.newInstance() это new выполняет проверку типа параметра метода, упаковку, и разрешение метода. Ни один из них не происходит в отражении, где явный выбор должен быть сделан.

IllegalAccessException, Пытаясь Вызвать Недоступного Конструктора

IllegalAccessException может быть брошен, если попытка предпринимается, чтобы вызвать частное или иначе недоступного конструктора. ConstructorTroubleAccess пример иллюстрирует получающуюся трассировку стека.


import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

class Deny {
    private Deny() {
	System.out.format("Deny constructor%n");
    }
}

public class ConstructorTroubleAccess {
    public static void main(String... args) {
	try {
	    Constructor c = Deny.class.getDeclaredConstructor();
//  	    c.setAccessible(true);   // solution
	    c.newInstance();

        // production code should handle these exceptions more gracefully
	} catch (InvocationTargetException x) {
	    x.printStackTrace();
	} catch (NoSuchMethodException x) {
	    x.printStackTrace();
	} catch (InstantiationException x) {
	    x.printStackTrace();
	} catch (IllegalAccessException x) {
	    x.printStackTrace();
	}
    }
}
$ java ConstructorTroubleAccess
java.lang.IllegalAccessException: Class ConstructorTroubleAccess can not access
  a member of class Deny with modifiers "private"
        at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:65)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:505)
        at ConstructorTroubleAccess.main(ConstructorTroubleAccess.java:15)

Подсказка: ограничение доступа существует, который предотвращает отражающий вызов конструкторов, которые обычно не были бы доступны через прямой вызов. (Это включает, но не ограничивается, частные конструкторы в отдельном class и общедоступные конструкторы в отдельном частном class.) Однако, Constructor как объявляют, расширяется AccessibleObject который обеспечивает возможность подавить эту проверку через AccessibleObject.setAccessible().

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

Предыдущая страница: Создание Новых Экземпляров Класса
Следующая страница: Массивы и Перечислимые типы