wake-up-neo.net

Zugriff auf nicht sichtbare Klassen mit Reflektion

Ich versuche, eine Instanz einer nicht sichtbaren Klasse, der AKA-Paketklasse, mithilfe von Reflection zu erhalten. Ich habe mich gefragt, ob es eine Möglichkeit gibt, die Modifizierer zu ändern, um sie öffentlich zu machen, und dann mit Class.forName darauf zugreifen. Wenn ich das jetzt versuche, kann ich nicht sagen, dass ich es nicht tun kann. Leider gibt es keine setAccesible-Methode der Class-Klasse.

37
Josh Sobel

verschachtelte Klasse - Klasse, die in einer anderen Klasse definiert ist (einschließlich statischer und nicht statischer Klassen)
innere Klasse - nicht statische verschachtelte Klasse (Instanz der inneren Klasse muss Instanz der äußeren Klasse sein)

nicht verschachtelte Klassen (oberste Ebene)

Aufgrund Ihrer Frage wissen wir, dass der Konstruktor, auf den Sie zugreifen möchten, nicht öffentlich ist. So könnte Ihre Klasse aussehen (A Klasse ist in einem anderen Paket als unserem enthalten)

package package1;

public class A {
    A(){
        System.out.println("this is non-public constructor");
    }
}

Um eine Instanz dieser Klasse zu erstellen, müssen wir den Konstruktor aufrufen, den wir aufrufen möchten, und ihn zugänglich machen. Wenn dies erledigt ist, können wir Constructor#newInstance(arguments) verwenden, um eine Instanz zu erstellen.

Class<?> c = Class.forName("package1.A");
//full package name --------^^^^^^^^^^
//or simpler without Class.forName:
//Class<package1.A> c = package1.A.class;

//In our case we need to use
Constructor<?> constructor = c.getDeclaredConstructor();
//note: getConstructor() can return only public constructors
//so we needed to search for any Declared constructor

//now we need to make this constructor accessible
constructor.setAccessible(true);//ABRACADABRA!

Object o = constructor.newInstance();

verschachtelte und innere Klassen

Wenn Sie mit Class.forName auf verschachtelte (statische und nicht statische) Klassen zugreifen möchten, müssen Sie die folgende Syntax verwenden:

Class<?> clazz = Class.forName("package1.Outer$Nested");

Outer$Nested gibt an, dass die Klasse Nested in der Klasse Outer deklariert ist. Verschachtelte Klassen sind Methoden sehr ähnlich. Sie haben Zugriff auf alle Mitglieder der äußeren Klasse (einschließlich privater).

Aber wir müssen uns daran erinnern, dass die Instanz der inneren Klasse, um zu existieren, die Instanz ihrer äußeren Klasse erfordert. Normalerweise erstellen wir sie über:

Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();

wenn Sie also sehen, dass jede Instanz der inneren Klasse Informationen über ihre äußere Klasse hat (die Referenz auf diese äußere Instanz ist im Feld this$0 gespeichert, weitere Informationen: Was bedeutet es, wenn eine Variable den Namen hat?) this $ 0 "in IntelliJ IDEA beim Debuggen von Java? )

Wenn Sie also eine Instanz der Klasse Inner mit Constructor#newInstance() erstellen, müssen Sie als erstes Argument einen Verweis auf die Instanz der Klasse Outer übergeben (um das Verhalten von outer.new Inner() zu simulieren).

Hier ist ein Beispiel.

in package1

package package1;

public class Outer {
    class Inner{
        Inner(){
            System.out.println("non-public constructor of inner class");
        }
    }
}

in package2

package package2;

import package1.Outer;
import Java.lang.reflect.Constructor;

public class Test {
    public static void main(String[] args) throws Exception {

        Outer outerObject = new Outer();

        Class<?> innerClazz = Class.forName("package1.Outer$Inner");

        // constructor of inner class as first argument need instance of
        // Outer class, so we need to select such constructor
        Constructor<?> constructor = innerClazz.getDeclaredConstructor(Outer.class);

        //we need to make constructor accessible 
        constructor.setAccessible(true);

        //and pass instance of Outer class as first argument
        Object o = constructor.newInstance(outerObject);

        System.out.println("we created object of class: "+o.getClass().getName());

    }
}

statisch verschachtelte Klassen

Instanzen von statisch verschachtelten Klassen erfordern keine Instanz der Outer-Klasse (da sie statisch sind). In diesem Fall brauchen wir also nicht nach Konstruktoren mit Outer.class als erstem Argument zu suchen. Und wir müssen keine Instanz der äußeren Klasse als erstes Argument übergeben. Mit anderen Worten, der Code ist derselbe wie für nicht verschachtelte Klassen (oberste Ebene) (möglicherweise mit der Ausnahme, dass Sie in Class.forName() die $Nested -Syntax hinzufügen müssten).

57
Pshemo

Class.forName sollte funktionieren. Wenn sich die Klasse in einer Pakethierarchieliste in der Sicherheitseigenschaft "package.access" befindet, müssen Sie den Vorgang mit den entsprechenden Berechtigungen ausführen (normalerweise alle Berechtigungen; oder keinen Sicherheitsmanager).

Wenn Sie versuchen, Class.newInstance zu verwenden, tun Sie dies nicht. Class.newInstance behandelt Ausnahmen schlecht. Rufen Sie stattdessen eine Constructor auf und rufen Sie newInstance auf. Es ist schwierig zu erkennen, womit Sie Probleme haben, ohne die Ausnahmespur.

Nach wie vor sind die meisten, aber nicht alle Zwecke der Reflexion schlechte Ideen.

Ich hatte die Anforderung, den Wert von Feld aus einer älteren Objektversion zu kopieren, wenn der Wert in der neuesten Version null ist. Wir hatten diese 2 Möglichkeiten. 

Core Java: 

for (Field f : object.getClass().getSuperclass().getDeclaredFields()) {
    f.setAccessible(true);
  System.out.println(f.getName());
  if (f.get(object) == null){
    f.set(object, f.get(oldObject));
  }
}

Spring [org.springframework.beans.BeanWrapper] verwenden:

BeanWrapper bw = new BeanWrapperImpl(object);
PropertyDescriptor[] data = bw.getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : data) {
  System.out.println(propertyDescriptor.getName());
  Object propertyValue = bw.getPropertyValue(propertyDescriptor.getName());
  if(propertyValue == null )
    bw.setPropertyValue( new PropertyValue(propertyDescriptor.getName(),"newValue"));
}
0
Quest_for_java

Sie können Manifolds@Jailbreak für die direkte, typsichere Java-Reflektion verwenden:

Foo foo = new @Jailbreak Foo();

public class Foo {
    Foo() {...}

    private void yodog() {...}
}

Hier ermöglicht @Jailbreak dem Compiler, den Konstruktor typsicher aufzulösen, als wäre er öffentlich, während Manifold unter der Haube einen effizienten Reflexionscode für Sie generiert.

Zusätzlich können Sie mit @Jailbreak auf nicht sichtbare Klassen zugreifen und diese erstellen:

com.abc. @Jailbreak Bar bar = new com.abc. @Jailbreak Bar();

package com.abc;
// package-private class
class Bar {
    Bar() {...}
}

Für den Zugriff auf ausgeblendete Klassen erfordert die Anmerkungsgrammatik von Java, dass die Klasse außerhalb ihres Pakets kommentiert wird.

Im Allgemeinen können Sie @Jailbreak für jede Art von Reflexion verwenden:

@Jailbreak Foo foo = new Foo();
foo.yodog();

@Jailbreak gibt die lokale Variable foo im Compiler für den direkten Zugriff auf alle Member in der Hierarchie von Foo frei.

Ebenso können Sie die Erweiterungsmethode jailbreak () zur einmaligen Verwendung verwenden:

foo.jailbreak().yodog();

Über die jailbreak()-Methode können Sie auf jedes Mitglied in der Hierarchie von Foo zugreifen.

Erfahren Sie mehr über Manifold .

0
Scott McKinney

Wir haben vor kurzem eine Bibliothek veröffentlicht, die durch Reflexion viel auf private Felder, Methoden und innere Klassen zugreifen kann: BoundBox

Für eine Klasse wie 

public class Outer {
    private static class Inner {
        private int foo() {return 2;}
    }
}

Es bietet eine Syntax wie: 

Outer outer = new Outer();
Object inner = BoundBoxOfOuter.boundBox_new_Inner();
new BoundBoxOfOuter.BoundBoxOfInner(inner).foo();

Das einzige, was Sie zum Erstellen der BoundBox-Klasse tun müssen, ist @BoundBox(boundClass=Outer.class) zu schreiben, und die BoundBoxOfOuter-Klasse wird sofort generiert.

0
Snicolas