web-dev-qa-db-ja.com

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?
    }        
}

名前を付けてBarクラスの新しいインスタンスを作成することは可能ですか?私が使用しようとしました:

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

クラスを見つけますが、c.newInstance()を使用すると、InstantiationExceptionがスローされます。

34
kars7e

これを行うには、いくつかのフープをジャンプする必要があります。まず、 Class.getConstructor() を使用して、呼び出すConstructorオブジェクトを見つける必要があります。

このClassオブジェクトが表すクラスの指定されたパブリックコンストラクターを反映するConstructorオブジェクトを返します。 parameterTypesパラメータは、宣言された順序でコンストラクタの仮パラメータタイプを識別するClassオブジェクトの配列です。 このClassオブジェクトが非静的コンテキストで宣言された内部クラスを表す場合、仮パラメーター型には、最初のパラメーターとして明示的な囲みインスタンスが含まれます。

そして、あなたは Constructor.newInstance() を使用します:

コンストラクターの宣言クラスが非静的コンテキストの内部クラスである場合、コンストラクターの最初の引数は囲んでいるインスタンスである必要があります

54
skaffman

確かに、最初に親クラスを作成しないと、内部クラスを作成できません。親クラスの外に存在することはできません。リフレクションを行うときは、親クラスのインスタンスを渡す必要があります。ネストされたクラスはstaticであり、親クラスから独立して使用できるため、リフレクションを実行するときにも使用できます。

[〜#〜] 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[] {});
            }
        }
    }
}

これは生成するはずです

ネスト構造
内部構造
ネスト構造
内部構造
ネスト構造
25
BalusC

速くて汚いコード:

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

説明:囲んでいるFooについてバーに伝える必要があります。

7
meriton

はい。外部インスタンスを内部クラスにフィードする必要があることに注意してください。 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."<init>":()V
   9:   return

他の回答では、やりたいことに対してどのようにできるかが説明されています。

ただし、これを実行する必要があるという事実は、システム設計に少し問題があることを示していることをお伝えしておきます。外側のクラスで(非静的)ファクトリメソッドが必要か、内部クラスを静的として宣言する必要があることをお勧めします。

(静的ではない)内部クラスインスタンスを作成すると、カプセル化の「匂い」が反映されます。

1
Stephen C

これは完全に最適というわけではありませんが、内部クラスと内部静的クラスの深さで機能します。

public <T> T instantiateClass( final Class<T> cls ) throws CustomClassLoadException {
    try {
        List<Class<?>> toInstantiate = new ArrayList<Class<?>>();
        Class<?> parent = cls;
        while ( ! Modifier.isStatic( parent.getModifiers() ) && parent.isMemberClass() ) {
            toInstantiate.add( parent );
            parent = parent.getDeclaringClass();
        }
        toInstantiate.add( parent );
        Collections.reverse( toInstantiate );
        List<Object> instantiated = new ArrayList<Object>();
        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 );
    }
}
0
mrswadge