簡體   English   中英

是否可以使用Java Reflection創建嵌套類的實例?

[英]Is it possible to create an instance of nested class using 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。

你需要跳過幾個箍才能做到這一點。 首先,您需要使用Class.getConstructor()來查找要調用的Constructor對象:

返回一個Constructor對象,該對象反映此Class對象所表示的類的指定公共構造函數。 parameterTypes參數是一個Class對象數組,它按聲明的順序標識構造函數的形式參數類型。 如果此Class對象表示在非靜態上下文中聲明的內部類,則形式參數類型包括顯式封閉實例作為第一個參數。

然后使用Constructor.newInstance()

如果構造函數的聲明類是非靜態上下文中的內部類,則構造函數的第一個參數需要是封閉的實例

如果不首先構造父類,確實不能構造內部類。 它不能存在於父類之外。 在進行反射時,您必須傳遞父類的實例。 嵌套類是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[] {});
            }
        }
    }
}

這應該產生

Nested constructed
Inner constructed
Nested constructed
Inner constructed
Nested constructed

快速而臟的代碼:

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

說明:您必須告訴Bar其封閉的Foo。

是。 請記住,您需要將外部實例提供給內部類。 使用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

其他答案已經解釋了如何做到你想做的事情。

但我想告訴你,你需要這樣做的事實表明你的系統設計有點不對勁。 我建議您在封閉類上需要(非靜態)工廠方法,或者需要將內部類聲明為靜態。

反射地創建(非靜態)內部類實例具有破壞封裝的“氣味”。

這不是完全最優的,但它適用於內部類和內部靜態類的深度。

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 );
    }
}

這里是嵌套類(靜態內部)的答案:在我的情況下,我需要通過其完全限定名稱獲取類型

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

' $ '至關重要!

使用一個圓點,您將獲得類“package.innerClass.outerClass”的ClassNotFoundException。 例外是誤導:-(。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM