61

リフレクションを使用して、非表示クラス、別名パッケージプライベートクラスのインスタンスを取得しようとしています。修飾子を切り替えて公開し、を使用してアクセスする方法があるかどうか疑問に思いましたClass.forName。私が今それを試みるとき、それは私がそれをすることができないと言うのを止めます。残念ながら、クラスのsetAccesibleメソッドはありません。Class

4

6 に答える 6

72

ネストされたクラス-他のクラス内で定義されたクラス(静的クラスと非静的クラスを含む)
内部クラス-非静的ネストされたクラス(内部クラスのインスタンスには外部クラスのインスタンスが存在する必要があります)

##ネストされていない(トップレベル)クラス

あなたの質問に基づいて、アクセスしたいコンストラクターは公開されていないことがわかります。したがって、クラスは次のようになります(Aクラスは私たちのものとは異なるパッケージに含まれています)

package package1;

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

このクラスのインスタンスを作成するには、呼び出したいコンストラクターにアクセスして、アクセス可能にする必要があります。完了するConstructor#newInstance(arguments)と、インスタンスの作成に使用できます。

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

##ネストされたクラスと内部クラス

ネストされた(静的および非静的)クラスにアクセスする場合Class.forNameは、構文を使用する必要があります。

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

Outer$NestedNestedクラスはクラス内で宣言されていると言いますOuter。ネストされたクラスはメソッドと非常によく似ており、外部クラスのすべてのメンバー(プライベートクラスを含む)にアクセスできます。

ただし、存在する内部クラスのインスタンスには、その外部クラスのインスタンスが必要であることを覚えておく必要があります。通常、次の方法で作成します。

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

ご覧のとおり、Innerクラスの各インスタンスには、その外部クラスに関する情報があります(その外部インスタンスへの参照はthis$0フィールドに格納されます。詳細:Javaのデバッグ中にIntelliJIDEAで変数の名前が「this$0」の場合はどういう意味ですか?

したがって、クラスのインスタンスを作成するInnerときConstructor#newInstance()に、最初の引数としてクラスのインスタンスへの参照を渡す必要がありますOuter(動作をシミュレートするouter.new Inner()ため)。

これが例です。

package1に

package package1;

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

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

    }
}

##静的にネストされたクラス

静的にネストされたクラスのインスタンスは、Outerクラスのインスタンスを必要としません(静的であるため)。Outer.classしたがって、彼らの場合、最初の引数としてコンストラクターを探す必要はありません。そして、最初の引数として外部クラスのインスタンスを渡す必要はありません。言い換えると、コードはネストされていない(トップレベルの)クラスの場合と同じになります(たとえば、のようにネストされたクラスを参照するときに、クラス名$の代わりにuseを追加する必要があるという事実を除いて)。.Class.forName()Class.forName("some.package.Outer$Nested1$NestedNested")

于 2013-02-22T02:28:21.787 に答える
3

Class.forName動作するはずです。クラスがセキュリティプロパティのパッケージ階層リスト内にある場合は"package.access"、適切な権限(通常はすべての権限、またはセキュリティマネージャがない)で操作を実行する必要があります。

使用しようとしている場合は、使用Class.newInstanceしないでください。Class.newInstance例外の処理が不十分です。代わりに、を取得してConstructorそれを呼び出しますnewInstance。例外トレースがないと、問題が発生していることを確認するのは困難です。

相変わらず、リフレクションのすべてではありませんがほとんどの使用法は悪い考えです。

于 2013-02-22T02:15:03.037 に答える
1

最近、リフレクションを介してプライベートフィールド、メソッド、および内部クラスにアクセスするのに役立つライブラリをリリースしました:BoundBox

のようなクラスの場合

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

次のような構文を提供します。

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

BoundBoxクラスを作成するためにあなたがしなければならない唯一のことは書くこと@BoundBox(boundClass=Outer.class)であり、BoundBoxOfOuterクラスは即座に生成されます。

于 2013-09-26T05:03:33.370 に答える
1

Manifoldの @Jailbreakを使用して、タイプセーフなJavaリフレクションを直接実行できます。

Foo foo = new @Jailbreak Foo();

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

    private void yodog() {...}
}

これにより、コンパイラーはコンストラクターをパブリックのようにタイプセーフに解決できます@Jailbreakが、Manifoldは内部で効率的なリフレクションコードを生成します。

さらに@Jailbreak、非表示クラスにアクセスして構築するために使用できます。

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

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

非表示のクラスアクセスの場合、Javaの注釈文法では、パッケージとは別にクラスに注釈を付ける必要があります。

@Jailbreakより一般的には、あらゆるタイプの反射に使用できます。

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

@JailbreakFooコンパイラのfooローカル変数のロックを解除して、の階層内のすべてのメンバーに直接アクセスできるようにします。

同様に、jailbreak()拡張メソッドを1回限りの使用に使用できます。

foo.jailbreak().yodog();

このメソッドを介して、の階層内のjailbreak()任意のメンバーにアクセスできます。Foo

マニホールドの詳細をご覧ください。

于 2018-12-09T04:16:51.177 に答える
0

最新バージョンで値がnullの場合、古いバージョンのオブジェクトからフィールドの値をコピーする必要がありました。これらの2つのオプションがありました。

コア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]:

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"));
}
于 2014-11-26T07:53:02.583 に答える
0

ここでAndroidで同様のことをしなければならなかったのは、私が思いついたものです。

/**
 * To fix issues with wrong edge-to-edge bottom sheet top padding and status bar icon color…
 * …we need to call [BottomSheetDialog.EdgeToEdgeCallback.setPaddingForPosition] which is a private function from a private class.
 * See: https://github.com/material-components/material-components-android/issues/2165
 */
fun adjustBottomSheet(aDialog : BottomSheetDialog) {
    // Get our private class
    val classEdgeToEdgeCallback = Class.forName("com.google.android.material.bottomsheet.BottomSheetDialog\$EdgeToEdgeCallback")
    // Get our private method
    val methodSetPaddingForPosition: Method = classEdgeToEdgeCallback.getDeclaredMethod("setPaddingForPosition", View::class.java)
    methodSetPaddingForPosition.isAccessible = true
    // Get private field containing our EdgeToEdgeCallback instance
    val fieldEdgeToEdgeCallback = BottomSheetDialog::class.java.getDeclaredField("edgeToEdgeCallback")
    fieldEdgeToEdgeCallback.isAccessible = true
    // Get our bottom sheet view field
    val fieldBottomField = BottomSheetDialog::class.java.getDeclaredField("bottomSheet")
    fieldBottomField.isAccessible = true
    // Eventually call setPaddingForPosition from EdgeToEdgeCallback instance passing bottom sheet view as parameter
    methodSetPaddingForPosition.invoke(fieldEdgeToEdgeCallback.get(aDialog),fieldBottomField.get(aDialog))
}

また、proguardがアクセスしようとしているメソッドを破棄しないようにすることもできます。

# Needed for now to fix our bottom sheet issue from 
com.google.android.material:material:1.4.0-alpha02
-keep class com.google.android.material.bottomsheet.BottomSheetDialog$EdgeToEdgeCallback {
    private void setPaddingForPosition(android.view.View);
}
于 2021-04-09T09:57:13.167 に答える