是否可以使用 Java Reflection 创建内部类的实例?

发布于 2024-08-18 02:31:59 字数 498 浏览 5 评论 0原文

代码示例:

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。

Sample of code:

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

Is it possible to create new instance of class Bar giving its name? I tried to use:

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

it finds the class, but when i use c.newInstance() it throws InstantiationException.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

单挑你×的.吻 2024-08-25 02:31:59

你需要跳过一些障碍才能做到这一点。首先,您需要使用 Class.getConstructor() 查找要调用的 Constructor 对象:

返回一个构造函数对象
反映特定公众
所表示的类的构造函数
通过这个 Class 对象。这
parameterTypes 参数是一个数组
标识的类对象
构造函数的形参类型,
按宣布的顺序。 如果这个类
对象代表一个内部类
在非静态上下文中声明,
形式参数类型包括
显式封闭实例作为
第一个参数。

然后使用Constructor.newInstance()

如果构造函数声明类
是非静态的内部类
上下文,第一个参数
构造函数必须是封闭的
实例

You need to jump through a few hoops to do this. First, you need to use Class.getConstructor() to find the Constructor object you want to invoke:

Returns a Constructor object that
reflects the specified public
constructor of the class represented
by this Class object. The
parameterTypes parameter is an array
of Class objects that identify the
constructor's formal parameter types,
in declared order. If this Class
object represents an inner class
declared in a non-static context, the
formal parameter types include the
explicit enclosing instance as the
first parameter.

And then you use Constructor.newInstance():

If the constructor's declaring class
is an inner class in a non-static
context, the first argument to the
constructor needs to be the enclosing
instance

一曲爱恨情仇 2024-08-25 02:31:59

如果不先构造父类,确实无法构造内部类。它不能存在于父类之外。进行反射时,您必须传递父类的实例。嵌套类是静态的,它们可以独立于父类使用,因此在进行反射时也是如此。

这是一个 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

Inner classes can indeed not be constructed without constructing the parent class first. It cannot exist outside the parent class. You'll have to pass an instance of the parent class in when doing reflection. Nested classes are static and they can be used independently from the parent class, thus also when doing reflection.

Here's an SSCCE which demonstrates all the stuff.

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

This should produce

Nested constructed
Inner constructed
Nested constructed
Inner constructed
Nested constructed
≈。彩虹 2024-08-25 02:31:59

快速而肮脏的代码:

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

说明:您必须告诉 Bar 其封闭的 Foo。

Quick and dirty code:

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

Explanation: You must tell the Bar about its enclosing Foo.

南薇 2024-08-25 02:31:59

是的。请记住,您需要将外部实例提供给内部类。使用 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

Yes. Remember you need to feed the outer instance to an inner class. Use javap to find the constructor. You will need to go through java.lang.reflect.Constructor rather than rely upon the evil 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 is interesting on the constructor because (assuming -target 1.4 or later, now implicit) you get an assignment of an instance field before calling the super constructor (used to be illegal).

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
時窥 2024-08-25 02:31:59

其他答案已经解释了如何才能完成您想做的事情。

但我想向您建议,您需要这样做的事实表明您的系统设计有些问题。我建议您要么需要封闭类上的(非静态)工厂方法,要么需要将内部类声明为静态。

反射性地创建(非静态)内部类实例具有封装损坏的“气味”。

Other answers have explained how you can to what you want to do.

But I want to suggest to you that the fact that you need to do this at all is an indication that there is something a bit wrong with your system design. I would suggest that you either need a (non-static) factory method on the enclosing class, or you need to declare the inner class as static.

Creating a (non-static) inner class instance reflectively has a "smell" of broken encapsulation.

泪之魂 2024-08-25 02:31:59

这并不完全是最佳的,但它适用于内部类和内部静态类的深度。

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

This isn't entirely optimal, but it works for depths of inner classes and inner static classes.

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 );
    }
}
岁月静好 2024-08-25 02:31:59

这是嵌套类(静态内部)的答案:
就我而言,我需要通过其完全限定名称获取类型,

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

$”至关重要!

如果有一个点,你会得到类“package.innerClass.outerClass”的 ClassNotFoundException 异常。该异常具有误导性:-(。

Here a answer for nested class (static inner):
In my case i need to acquire the type by its fully qualified name

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

the '$' is crucial!

with a dot you'll get ClassNotFoundException for class "package.innerClass.outerClass". The exception is missleading :-(.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文