次のような Java ランタイム コンパイラがあります。
public class Compiler {
private final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
private final Map<String, String> source = new HashMap<String, String>();
private final MemoryFileManager manager = new MemoryFileManager(this.compiler);
public void add(String classname, String fileContent) {
add(Collections.singletonMap(classname, fileContent));
}
public void add(Map<String, String> map) {
source.putAll(map);
}
public void compile() {
List<Source> list = new ArrayList<Source>();
for (Map.Entry<String, String> entry : source.entrySet()) {
list.add(new Source(entry.getKey(), JavaFileObject.Kind.SOURCE, entry.getValue()));
}
this.compiler.getTask(null, this.manager, null, null, null, list).call();
}
public byte[] getByteCode(String name) {
return this.manager.map.get(name).toByteArray();
}
}
付随する単純なクラスローダーの例:
public class SimpleClassLoader extends ClassLoader {
private String classname;
private byte[] byteCode;
public SimpleClassLoader(String classname, byte[] byteCode) {
super(SimpleClassLoader.class.getClassLoader());
this.classname = classname;
this.byteCode = byteCode;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if(name.equals(classname)){
return defineClass(name, byteCode, 0, byteCode.length);
}
return super.findClass(name);
}
}
クラスをコンパイルしてロードすると、結果のクラスのパッケージ名がクラス名に連結されます。
Compiler compiler = new Compiler();
String className = "example.test.TestClass";
String source = "package example.test; public class TestClass{}";
compiler.add(className, source);
compiler.compile();
byte[] byteCode = compiler.getByteCode(className);
Class<?> aClass = Class.forName(className, true, new SimpleClassLoader(className, byteCode));
System.out.println("Package: " + aClass.getPackage()); // Should be "example.text"
System.out.println("Name: " + aClass.getSimpleName()); // Should be "TestClass"
これの出力は予想外に次のようになります。
Package: null
Name: TestClass
ここでどこが間違っていますか?