Spec-Zone .ru
спецификации, руководства, описания, API
|
Со следующими проблемами иногда встречаются разработчики, пытаясь вызвать конструкторов через отражение.
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
перестал бы работать. 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()
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
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)
Constructor
как объявляют, расширяется AccessibleObject
который обеспечивает возможность подавить эту проверку через AccessibleObject.setAccessible()