Somehow, I don't like this sort of questions. It's from a lecture back in 2005, and according to websearches, it seems that nobody has found "the" solution until now, and no solution has been published.
The constraints are clear, but the question of what is allowed or not is somewhat fuzzy. Every solution could be considered as "cheating", in one or the other way, because a class with a private constructor is not meant to be subclassed. That's a critical security mechanism, and the responsible engineers are working hard to make sure that this security mechanism cannot be trivially circumvented.
So of course, you have to cheat in order to solve this.
Nevertheless, I spent quite a while with this, and here's how I eventually cheated it:
1.) Download the Apache Bytecode Engineering Library, and place the bcel-6.0.jar
in one directory.
2.) Create a file CreateB.java
in the same directory, with the following contents:
import java.io.FileOutputStream;
import org.apache.bcel.Const;
import org.apache.bcel.generic.*;
public class CreateB
{
public static void main(String[] args) throws Exception
{
ClassGen cg = new ClassGen("B", "A", "B.java",
Const.ACC_PUBLIC | Const.ACC_SUPER, new String[] {});
ConstantPoolGen cp = cg.getConstantPool();
InstructionList il = new InstructionList();
MethodGen method = new MethodGen(Const.ACC_PUBLIC, Type.VOID,
Type.NO_ARGS, new String[] {}, "<init>", "B", il, cp);
il.append(InstructionFactory.createReturn(Type.VOID));
method.setMaxStack();
method.setMaxLocals();
cg.addMethod(method.getMethod());
il.dispose();
cg.getJavaClass().dump(new FileOutputStream("B.class"));
}
}
3.) Compile and execute this class:
javac -cp .;bcel-6.0.jar CreateB.java
java -cp .;bcel-6.0.jar CreateB
(note: On linux, the ;
must be a :
). The result will be a file B.class
.
4.) Copy the class that was given in the question (verbatim - without any modification) into the same directory and compile it.
5.) Create the following class in the same directory, and compile it:
public class TestA
{
public static void main(String[] args)
{
A.test(new B());
}
}
6.) The crucial step: Call
java -Xverify:none TestA
The output will be Here!
.
The key point is that the CreateB
class creates a class B
that extends A
, but does not invoke the super constructor. (Note that an implicit super constructor invocation would normally be added by the compiler. But there's no compiler involved here. The bytecode is created manually). All this would usually fail with a VerifyError
when the class is loaded, but this verification can be switched off with -Xverify:none
.
So in summary:
- The class A itself is not edited (and also its byte code is not edited, I hope this is clear!)
- No reflection
- No serialization
- No custom native methods