Question

I'm writing an application which requires the dynamic implementation of an interface. I had no (obvious) issues regarding generating the class: I verified it with both javap and a decompiler.

The problem is, after I generate the class I define and instantiate. Finally, I cast it to the type of the implemented interface.

The problem is that I am being hit with a java.lang.ClassCastException: MyGeneratedClass cannot be cast to MyInterface, though I do add MyInterface as an interface when I call ClassWriter.visit.

Below is an SSCCE demonstrating the issue for an interface named MyInterface full of void methods.

ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);

cw.visit(V1_6,
        ACC_PUBLIC | ACC_SUPER,
        "MyGeneratedClass",
        null,
        "java/lang/Object",
        new String[]{MyInterface.class.getName().replace(".", "/"});

{
    MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
    mv.visitVarInsn(ALOAD, 0);
    mv.visitMethodInsn(INVOKESPECIAL,
            "java/lang/Object",
            "<init>",
            "()V");
    mv.visitInsn(RETURN);
    mv.visitMaxs(0, 0);
    mv.visitEnd();
}

for (Method call : MyInterface.class.getDeclaredMethods()) {
    MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, call.getName(), Type.getMethodDescriptor(call),  null, null);
    // ...
    mv.visitInsn(RETURN);
    mv.visitMaxs(0, 0);
    mv.visitEnd();
}
cw.visitEnd();
byte[] raw = cw.toByteArray();

try {
    return (MyInterface) new ClassLoader() {
        public Class defineClass(byte[] bytes) {
            return super.defineClass("MyGeneratedClass", bytes, 0, bytes.length);
        }
    }.defineClass(raw).newInstance();
} catch (InstantiationException | IllegalAccessException e) {
    e.printStackTrace();
    return null;
}

Printing out the generatedObject.getClass().getInterfaces() does indeed show that MyInterface is implemented. But generatedObject instanceof MyInterface returns false, something which I find a contradiction.

Can anyone shed some light on whats happening here? Any help would be greatly appreciated.

Was it helpful?

Solution

I'd guess it's because the new classloader isn't a child of the classloader that loaded MyInterface, which means you end up with two different classes which aren't the same despite being to all appearances identical. Try changing

return (MyInterface) new ClassLoader() {

to

return (MyInterface) new ClassLoader(MyInterface.class.getClassLoader()) {
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top