significato - reflection java 8




È possibile creare un'istanza di classe nidificata utilizzando Java Reflection? (5)

Esempio di codice:

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

È possibile creare una nuova istanza di Class Bar dando il suo nome? Ho provato a usare:

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

trova la classe, ma quando uso c.newInstance () lancia InstantiationException.


È necessario saltare attraverso alcuni cerchi per farlo. Innanzitutto, è necessario utilizzare Class.getConstructor() per trovare l'oggetto Constructor che si desidera richiamare:

Restituisce un oggetto Constructor che riflette il costruttore pubblico specificato della classe rappresentata da questo oggetto Class. Il parametro parameterTypes è un array di oggetti Class che identificano i tipi di parametri formali del costruttore, nell'ordine dichiarato. Se questo oggetto di classe rappresenta una classe interna dichiarata in un contesto non statico, i tipi di parametri formali includono l'istanza di inclusione esplicita come primo parametro.

E poi usi Constructor.newInstance() :

Se la classe dichiarante del costruttore è una classe interna in un contesto non statico, il primo argomento del costruttore deve essere l'istanza che lo include


Altre risposte hanno spiegato come è possibile ciò che si vuole fare.

Ma voglio suggerirvi che il fatto che sia necessario farlo è un'indicazione che c'è qualcosa di sbagliato nella progettazione del sistema. Suggerirei di aver bisogno di un metodo factory (non statico) sulla classe che lo include, o di dichiarare la classe interna come statica.

La creazione di un'istanza di classe interna (non statica) riflette in modo riflessivo un "odore" di incapsulamento rotto.


Ecco una risposta per la classe annidata (static inner): Nel mio caso ho bisogno di acquisire il tipo con il suo nome completo

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

il ' $ ' è fondamentale!

con un punto otterrai ClassNotFoundException per la classe "package.innerClass.outerClass". L'eccezione è fuorviante :-(.


Le classi interne non possono essere effettivamente costruite senza prima costruire la classe genitrice. Non può esistere al di fuori della classe genitore. Dovrai passare un'istanza della classe genitore quando effettui il reflection. Le classi annidate sono static e possono essere utilizzate indipendentemente dalla classe genitore, quindi anche quando si fa il reflection.

Ecco un SSCCE che dimostra tutte le cose.

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

Questo dovrebbe produrre

Nested constructed
Inner constructed
Nested constructed
Inner constructed
Nested constructed

Sì. Ricorda che devi alimentare l'istanza esterna a una classe interiore. Usa javap per trovare il costruttore. Dovrai passare attraverso java.lang.reflect.Constructor piuttosto che fare affidamento sul malvagio 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 è interessante sul costruttore perché (assumendo -target 1.4 o successivo, ora implicito) si ottiene un'assegnazione di un campo di istanza prima di chiamare il super costruttore (usato per essere illegale).

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




inner-classes