The rule that inner and outer classes can access their private members is a pure Java programming language construct that does not affect JVM access checks. When inner classes were introduced in Java 1.1, they were introduced in such a way that they did not require changes in the JVM. From the point of view of the JVM, nested classes are ordinary (upper) classes with some additional, uninformed meta-information.
When the inner class is declared private , its usual level of access to the class is "default" aka package-private. When protected declared, it will be public at the JVM level.
When nested classes access each other's private fields or methods, the compiler will generate synthetic helper methods with private access to the package in the target class, providing the desired access.
So, from the perspective of the JVM, you are trying to subclass a package-private class, and the dollar in the name is just an ordinary character in the name. The generated class has an appropriate qualified name, but you are trying to define it in another class loader, so the JVM considers these packages to be not identical at run time, despite their identical name.
You can check if access to the package level works if you define a class inside the same class loader. Change the line
Class<?> genClass = myClassLoader.defineClass("Parent$OtherChild", bytes);
to
Method m=ClassLoader.class.getDeclaredMethod( "defineClass", String.class, byte[].class, int.class, int.class); m.setAccessible(true); Class<?> genClass=(Class<?>)m.invoke( Child.class.getClassLoader(), "Parent$OtherChild", bytes, 0, bytes.length);
Alternatively, you can declare Child as protected . Starting with its low-level public class, it will be available to other class loaders.
Note that in both cases, you created a new inner class, but only a class called Parent$OtherChild that extends the inner class. The only difference is meta-information about the relationship between outer and inner classes, but if you add this attribute to your generated class, claiming that it is an inner Parent class, it may happen that it is rejected by the verifier because the meta-information from Parent does not mention the existence inner class of OtherChild . This is the only place the JVM can ever look at this attribute.
But, in addition to the relationship between inner classes of Reflection, in any case there is no functional difference between top-level classes and nested classes. As already mentioned, classes actually do not have protected and private access levels, and for all other access users you still need to generate the necessary code. If you cannot change the code of existing Parent or Parent$Child classes, you cannot access those of your private members for which these synthetic access methods do not yet exist ...
Starting with Java 9, there is a standard way to define a new class in an accessible context, which makes the "Reflection with access override" approach shown above obsolete for this use case, for example. following works:
public class Parent { public void generateClass() { ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS); String superType = Type.getInternalName(Child.class); cw.visit(49, Opcodes.ACC_PUBLIC, "Parent$OtherChild", null, superType, null); MethodVisitor mv = cw.visitMethod(0, "<init>", "()V", null, null); mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitMethodInsn(Opcodes.INVOKESPECIAL, superType, "<init>", "()V", false); mv.visitInsn(Opcodes.RETURN); mv.visitMaxs(-1, -1); mv.visitEnd();