反映仅具有包级别访问权限的 class

Reflecting a class with only package level access

我不太确定如何反映只有包级访问权限的 class。我知道如何反映任何具有 public 访问权限的 class,但我不知道如何反映以下示例:

public class Main {
    public static void main(String[] args) {
        test t = new test();
        Constructor<one.oneimpl> con = one.oneimpl.class.getDeclaredConstructor(test.class);
        oneimpl o = con.newInstance(t);
        o.doIt();
    }
}

========================

package one;
// implementation class for mimicking android api
class oneimpl extends one {
    Test mTest;
    private oneimpl(test t){mTest = t;}
    public void doIt(){System.out.println("Do It!");}
    public void dontDoit(){System.out.println("Don't Do It!");}
}

========================

package one;
// abstract class for mimicking android api
abstract class one {
    public void doIt();
    public void dontDoIt();
}

========================

package one;
// empty class for mimicking android api
public class test {}

如您所见,class one 仅具有包级访问权限。这让我难以反映 class。我继续收到编译器错误,指出:

Main.java:4: error: oneimpl is not public in one; cannot be accessed from outside package
oneimpl o = con.newInstance(t);

我已经查看了一些帖子,自己解决了这个问题,但即使在阅读了大部分 "similar questions"、阅读了 AccessibilityObject api 并阅读了一般性反思之后我仍然不清楚如何实现这一点。

最终我想做的是反映 API 的特定部分来构建对象,以便 soot/spark 可以构建适当的调用图。我实际上并没有在 android API.

内部工作

您需要在每一步都使用反射才能完成您想要做的事情。

首先,由于您不能从包外部直接引用包 one 中的包范围 class,因此您需要动态加载 class(另请注意使用 ? 而不是直接使用 oneimpl):

Class<?> oneImplClass = Class.forName("one.oneimpl");

然后你可以获得构造函数和doIt方法(确保你将这两个都设置为可访问,否则你会得到运行时错误):

Constructor<?> constructor = oneImplClass.getDeclaredConstructor(test.class);
constructor.setAccessible(true);
Method doIt = oneImplClass.getDeclaredMethod("doIt");
doIt.setAccessible (true);

接下来,实例化 class。你必须将它转换为 Object 因为你不能直接引用 class):

Object oneImpl = (Object) constructor.newInstance();

最后,您可以调用 doIt 方法:

doIt.invoke(oneImpl);

最后一点:在命名 class 时,您应该遵循 Java 命名约定:oneimpl 应该是 OneImpltest 应该是Test。否则 one.oneimpl 之类的东西最终看起来像一个包名称而不是完全限定的 class 名称。

public static void main(String[] args) throws Exception {
    test t = new test();
    Class<?> oneimpl = Main.class.getClassLoader().loadClass("one.oneimpl");
    Constructor<?> con = oneimpl.getDeclaredConstructor(test.class);
    con.setAccessible(true);
    Object o = con.newInstance(t);
    Method m = oneimpl.getDeclaredMethod("doIt");
    m.setAccessible(true);
    m.invoke(o);
}