3

I'm not quite sure how to go about reflecting a class that only has package-level access. I know how to reflect any class that has public access, but I cannot figure out how to reflect the following example:

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

As you can see, the class one only has package-level access. This makes reflecting the class difficult for me. I continue to get compiler errors that states:

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

I've reviewed a few posts, to solve this on my own, but even after reading most of the "similar questions", reading the AccessibilityObject api, and reading generic reflection procedures it's still unclear to me how to achieve this.

Ultimately what I am trying to do is to reflect particular portions of the API to build objects so that soot/spark can build a proper call graph. I'm not actually working inside the android API.

lilott8
  • 1,116
  • 2
  • 17
  • 41
  • you need to call "setAccessible". see: http://stackoverflow.com/questions/5629706/java-accessing-private-constructor-with-type-parameters – slipperyseal May 12 '15 at 23:23
  • This particular example is quite a bit different than the link you provided. It is assumed that the classes given in the example are in the same package and the constructor is private. I have a public constructor with a package-level access class. My code won't even compile. See the above error generated. – lilott8 May 12 '15 at 23:27
  • Actually you have a private constructor, look closely at your declaration in `oneimpl`: `private oneimpl(test t){mTest = t;}`. And even if it was public you'd still have to call `setAccessible` since public constructors/methods in inaccessible classes are considered inaccessible. – neuronaut May 12 '15 at 23:50
  • Ha! that I do. That is a slight deviation from the android API that I am working on. In the end, the visibility of the constructor is a moot point. But thank you for bringing that to my attention! – lilott8 May 12 '15 at 23:56

2 Answers2

8

You'll need to use reflection at every step in order to do what you're trying to do.

First, since you can't directly reference a package-scoped class in package one from outside that package you'll need to load that class dynamically (also note the use of ? instead of directly using oneimpl):

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

Then you can get the constructor and the doIt method (make sure you set both of these as accessible, otherwise you'll get runtime errors):

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

Next, instantiate the class. You'll have to cast it to Object since you can't reference the class directly):

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

Lastly you can invoke the doIt method:

doIt.invoke(oneImpl);

As one last note: you should follow Java naming conventions when naming your classes: oneimpl should be OneImpl and test should be Test. Otherwise things like one.oneimpl end up looking like a package name rather than a fully qualified class name.

neuronaut
  • 2,689
  • 18
  • 24
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);
}
Dédé Lolo
  • 144
  • 4