反射一个仅限于包级别访问的类

3

我不太确定如何反映只具有包级访问权限的类。我知道如何反映任何具有公共访问权限的类,但我无法找出以下示例的反射方法:

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

正如您所看到的,类one只有包级别的访问权限。这使得对该类进行反射变得困难。我继续收到编译器错误消息,指出:

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

我已经查阅了一些帖子,试图自己解决这个问题,但即使阅读了大部分“类似问题”的内容、AccessibilityObject api和通用反射过程的内容,我仍然不清楚如何实现这一目标。
最终,我的目标是通过反映API的特定部分来构建对象,以便soot/spark可以构建正确的调用图。我实际上并不是在Android API内工作。

你需要调用“setAccessible”。请参考:https://dev59.com/PG035IYBdhLWcg3wGL9T - slipperyseal
这个具体例子与您提供的链接有很大不同。假设示例中给定的类在同一个包中,并且构造函数是私有的。我有一个带有包级访问类的公共构造函数。我的代码甚至无法编译。请参见上面生成的错误。 - lilott8
实际上,您有一个私有构造函数,请仔细查看您在oneimpl中的声明:private oneimpl(test t){mTest = t;}。即使它是公共的,您仍然必须调用setAccessible,因为不可访问的类中的公共构造函数/方法被视为不可访问。 - neuronaut
哈!我做到了。这是我正在工作的Android API的轻微偏差。最后,构造函数的可见性是一个无关紧要的问题。但感谢您提醒我! - lilott8
2个回答

8
您需要在每个步骤中使用反射才能完成您正在尝试的操作。
首先,由于您无法直接从包 one 外部引用包范围内的类,因此您需要动态加载该类(还请注意使用?而不是直接使用 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);

接下来,实例化该类。由于您无法直接引用该类,因此必须将其转换为Object

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

最后,您可以调用doIt方法:
doIt.invoke(oneImpl);

最后一点:在命名类时,您应该遵循Java的命名规范:oneimpl 应改为 OneImpltest 应改为 Test。否则,像 one.oneimpl 这样的东西看起来更像是一个包名而不是完全限定的类名。


所以我知道如何做,只是没有把所有的部分组合起来 :(。谢谢! - lilott8

1
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);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接