Можно ли создать экземпляр вложенного classа с помощью Java Reflection?

Пример кода:

public class Foo { public class Bar { public void printMesg(String body) { System.out.println(body); } } public static void main(String[] args) { // Creating new instance of 'Bar' using Class.forname - how? } } 

Возможно ли создать новый экземпляр classа Bar с его именем? Я попытался использовать:

 Class c = Class.forName("Foo$Bar") 

он находит class, но когда я использую c.newInstance (), он выдает ExpressionExceptionException.

7 Solutions collect form web for “Можно ли создать экземпляр вложенного classа с помощью Java Reflection?”

Для этого вам нужно проскользнуть через несколько обручей. Во-первых, вам нужно использовать Class.getConstructor (), чтобы найти объект Constructor вы хотите вызвать:

Возвращает объект Constructor, который отражает указанный публичный конструктор classа, представленного этим объектом classа. Параметр parameterTypes представляет собой массив объектов classа, которые идентифицируют формальные типы параметров конструктора в объявленном порядке. Если этот объект classа представляет собой внутренний class, объявленный в нестационарном контексте, формальные типы параметров include явный вмещающий экземпляр в качестве первого параметра.

И затем вы используете Constructor.newInstance () :

Если class объявления конструктора является внутренним classом в нестатистическом контексте, первым аргументом конструктора должен быть экземпляр окружения

Внутренние classы действительно не могут быть построены без создания родительского classа. Он не может существовать вне родительского classа. Вы должны будете передать экземпляр родительского classа при выполнении рефлексии. Вложенные classы static и их можно использовать независимо от родительского classа, а также при отражении.

Вот SSCCE, который демонстрирует все.

 package mypackage; import java.lang.reflect.Modifier; public class Parent { public static class Nested { public Nested() { System.out.println("Nested constructed"); } } public class Inner { public Inner() { System.out.println("Inner constructed"); } } public static void main(String... args) throws Exception { // Construct nested class the normal way: Nested nested = new Nested(); // Construct inner class the normal way: Inner inner = new Parent().new Inner(); // Construct nested class by reflection: Class.forName("mypackage.Parent$Nested").newInstance(); // Construct inner class by reflection: Object parent = Class.forName("mypackage.Parent").newInstance(); for (Class< ?> cls : parent.getClass().getDeclaredClasses()) { if (!Modifier.isStatic(cls.getModifiers())) { // This is an inner class. Pass the parent class in. cls.getDeclaredConstructor(new Class[] { parent.getClass() }).newInstance(new Object[] { parent }); } else { // This is a nested class. You can also use it here as follows: cls.getDeclaredConstructor(new Class[] {}).newInstance(new Object[] {}); } } } } 

Это должно привести к

 Вложенная конструкция
 Внутренняя конструкция
 Вложенная конструкция
 Внутренняя конструкция
 Вложенная конструкция

Быстрый и грязный код:

 Foo.Bar.class.getConstructors()[0].newInstance(new Foo()); 

Объяснение: Вы должны сообщить адвокату о его включении в Foo.

Да. Помните, что вам нужно кормить внешний экземпляр внутренним classом. Используйте javap для поиска конструктора. Вам нужно будет пройти через java.lang.reflect.Constructor а не полагаться на злой Class.newInstance .

 Compiled from "Foo.java" public class Foo$Bar extends java.lang.Object{ final Foo this$0; public Foo$Bar(Foo); public void printMesg(java.lang.String); } 

javap -c интересен для конструктора, потому что (предположив -target 1.4 или более поздний, теперь неявный), вы получаете назначение поля экземпляра перед вызовом супер-конструктора (который был незаконным).

 public Foo$Bar(Foo); Code: 0: aload_0 1: aload_1 2: putfield #1; //Field this$0:LFoo; 5: aload_0 6: invokespecial #2; //Method java/lang/Object."":()V 9: return 

Другие ответы объяснили, как вы можете делать то, что хотите.

Но я хочу предложить вам, что тот факт, что вам нужно сделать это вообще, является признаком того, что в вашем дизайне системы что-то не так. Я бы предположил, что вам нужен либо (нестатический) заводский метод для охватывающего classа, либо вам нужно объявить внутренний class статическим.

Создание (нестатического) экземпляра внутреннего classа рефлексивно имеет «запах» сломанной инкапсуляции.

Это не совсем оптимально, но оно работает для глубин внутренних classов и внутренних статических classов.

 public  T instantiateClass( final Class cls ) throws CustomClassLoadException { try { List> toInstantiate = new ArrayList>(); Class< ?> parent = cls; while ( ! Modifier.isStatic( parent.getModifiers() ) && parent.isMemberClass() ) { toInstantiate.add( parent ); parent = parent.getDeclaringClass(); } toInstantiate.add( parent ); Collections.reverse( toInstantiate ); List instantiated = new ArrayList(); for ( Class< ?> current : toInstantiate ) { if ( instantiated.isEmpty() ) { instantiated.add( current.newInstance() ); } else { Constructor< ?> c = current.getConstructor( instantiated.get( instantiated.size() - 1 ).getClass() ); instantiated.add( c.newInstance( instantiated.get( instantiated.size() - 1 ) ) ); } } return (T) instantiated.get( instantiated.size() - 1 ); } catch ( InstantiationException e ) { throw new CustomClassLoadException( "Failed to load class.", e ); } catch ( IllegalAccessException e ) { throw new CustomClassLoadException( "Failed to load class.", e ); } catch ( SecurityException e ) { throw new CustomClassLoadException( "Failed to load class.", e ); } catch ( NoSuchMethodException e ) { throw new CustomClassLoadException( "Failed to load class.", e ); } catch ( IllegalArgumentException e ) { throw new CustomClassLoadException( "Failed to load class.", e ); } catch ( InvocationTargetException e ) { throw new CustomClassLoadException( "Failed to load class.", e ); } } 

Вот ответ для вложенного classа (статический внутренний): В моем случае мне нужно получить тип по его полному имени

 Class.forName(somePackage.innerClass$outerClass).getConstructor().newInstance(); 

« $ » имеет решающее значение!

с точкой вы получите ClassNotFoundException для classа «package.innerClass.outerClass». Исключение составляет missleading :-(.

Interesting Posts
Давайте будем гением компьютера.