0

First, I'm looking for an answer in Kotlin, but I'm interacting with a Java library.

I need to get an instance from a private static nested class, derived from an instance of the surrounding superclass.

Given you have these (simplified) nested Java classes

public abstract class GLFWKeyCallback extends Callback implements GLFWKeyCallbackI {

  public static GLFWKeyCallback create(GLFWKeyCallbackI instance) {
    new Container(instance.address(), instance);
  }

  private static final class Container extends GLFWKeyCallback {

    private final GLFWKeyCallbackI delegate;

    Container(long functionPointer, GLFWKeyCallbackI delegate) {
      super(functionPointer);
      this.delegate = delegate;
    }
  }
}

I get back a Container instance as a GLFWKeyCallback, by way of another external method. You can think of this method as:

public static GLFWKeyCallback getCallback() {
  return GLFWKeyCallback.create(anInternalInstance)
}

in Kotlin:

val callback:GLFWKeyCallback = getCallback()

// I would now want to cast,
// or in other ways use callback
// as the GLFWKeyCallback.Container class it actually is.

val callbackAsContainer = callback as GLFWKeyCallback.Container // Error: Container is private

val ContainerClass = GLFWKeyCallback::class.nestedClasses.find { it.simpleName?.contains("Container") ?: false }!!
// Gives me a KClass<*> that I don't know how to use, can't find documentation for this kind of circumstance

// If using the class instance itself is not possible I would at least want to get the
// Container.delegate of GLFWKeyCallbackI

val delegateField = ContainerClass.memberProperties.findLast { it.name == "delegate" }!!
val fieldValue = field.get(callback)
// Error: Out-projected type 'KProperty1<out Any, Any?>' prohibits the use of 'public abstract fun get(receiver: T): R defined in kotlin.reflect.KProperty1'

Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Karlsson
  • 203
  • 3
  • 13
  • 1
    First, I'd want to be sure that you really need to refer to the private static nested class, as that smells bad.  (For one thing, it's not part of the public interface of the third-party library, and so could be changed/renamed/removed in future versions.  For another, mucking around with the internals of a library could cause it to break in unexpected ways.)  Of course, there are times when it really is necessary — but you need to be certain that there are no alternatives. – gidds Feb 02 '19 at 11:23
  • @gidds you are definately right, but this time I need to. – Karlsson Feb 02 '19 at 12:05

2 Answers2

2

Why you don't want to use Java reflection? You can use it also from Kotlin:

val callback = getCallback()
val field = callback::class.java.getDeclaredField("delegate")
field.isAccessible = true
val delegate = field.get(callback) as GLFWKeyCallbackI
Willi Mentzel
  • 27,862
  • 20
  • 113
  • 121
Andrei Tanana
  • 7,932
  • 1
  • 27
  • 36
1

You can still get the class via .getClass(). This example prints '5':

public class Example {
    public static void main(String[] args) throws Exception {
        Object o = Target.get();
        Field f = o.getClass().getDeclaredField("field");
        f.setAccessible(true);
        Integer i = (Integer) f.get(o);
        System.out.println(i);
    }
}

public class Target {
    public static Object get() { return new Inner(); }
    private static class Inner {
        private int field = 5;
    }
}

If you know precise names:

Class<?> c = Class.forName("com.foo.pkgof.Target$Inner");
c.getDeclaredField("field");

works. Note the dollar. That's the separator to use between 'outer' and 'inner'.

rzwitserloot
  • 85,357
  • 5
  • 51
  • 72
  • Thank you for the answer, I could not get it to work for my use case in kotlin though. I might just be missing something in translation. If I also was writing test cases in java it would most likely have worked out fine. – Karlsson Feb 02 '19 at 12:34
  • @Karlsson See https://kotlinlang.org/docs/reference/reflection.html – kotlin reflection isn't like java reflection, but that article explains how to get back to java reflection. – rzwitserloot Feb 02 '19 at 16:01