34 votes

Est-il possible de créer une instance de classe imbriquée à l'aide de Java Reflection?

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

Est-il possible de créer une nouvelle instance de la classe Bar donnant son nom? J'ai essayé d'utiliser:

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

il trouve la classe, mais lorsque j'utilise c.newInstance (), il lève InstantiationException.

54voto

skaffman Points 197885

Vous avez besoin de sauter à travers quelques cerceaux pour ce faire. Tout d'abord, vous devez utiliser la Classe.getConstructor() pour trouver l' Constructor objet que vous souhaitez appeler:

Retourne un objet Constructeur qui reflète le public spécifié constructeur de la classe représentée par cette Classe d'objet. L' parameterTypes paramètre est un tableau des objets de Classe qui permettent d'identifier le constructeur de paramètres formels types, déclarée commande. Si cette Classe objet représente un intérieur de classe déclaré dans un non-statique contexte, l' paramètre formel types comprennent l' explicite en joignant les premier paramètre.

Et puis vous utilisez Constructeur.newInstance():

Si le constructeur de la classe de déclaration est un intérieur de classe non statique contexte, le premier argument de la le constructeur doit être la enfermant exemple

25voto

BalusC Points 498232

Les classes internes peuvent en effet pas être construite sans construire le parent de la classe de première. Il ne peut pas exister en dehors de la classe parent. Vous aurez à passer une instance de la classe parent quand faire la réflexion. Classes imbriquées sont static et ils peuvent être utilisés indépendamment de la classe parent et, par conséquent, lorsque vous faites la réflexion.

Voici un SSCCE qui démontre toutes les choses.

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

Cela devrait produire

Imbriquée construit
Intérieure construit
Imbriquée construit
Intérieure construit
Imbriquée construit

7voto

meriton Points 30447

Code rapide et sale:

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

Explication: Vous devez informer la barre de la présence de Foo.

2voto

Oui. N'oubliez pas de nourrir l'extérieur de l'instance à l'intérieur de la classe. Utiliser javap de trouver le constructeur. Vous aurez besoin de passer par l' java.lang.reflect.Constructor plutôt que de compter sur le mal 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 est intéressant sur le constructeur parce que (en supposant -target 1.4 ou plus tard, maintenant implicite), vous obtenez une cession d'un champ d'instance avant l'appel de la super constructeur (utilisé pour être illégale).

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

1voto

Stephen C Points 255558

D'autres réponses ont expliqué comment vous pouvez faire ce que vous voulez faire.

Mais je tiens à vous suggérer que le fait que vous deviez le faire est une indication qu'il y a quelque chose qui cloche dans la conception de votre système. Je suggérerais que vous avez besoin d'une méthode de fabrique (non statique) sur la classe englobante ou de déclarer la classe interne en tant que statique.

La création réfléchissante d’une instance de classe interne (non statique) dégage une "odeur" d’encapsulation cassée.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X