How do I programmatically compile and instantiate a Java class? [duplicate]

不羁岁月 提交于 2019-11-25 22:08:40

问题


This question already has an answer here:

  • How do you dynamically compile and load external java classes? [duplicate] 2 answers

I have the class name stored in a property file. I know that the classes store will implement IDynamicLoad. How do I instantiate the class dynamically?

Right now I have

     Properties foo = new Properties();
    foo.load(new FileInputStream(new File(\"ClassName.properties\")));
    String class_name = foo.getProperty(\"class\",\"DefaultClass\");
    //IDynamicLoad newClass = Class.forName(class_name).newInstance();

Does the newInstance only load compiled .class files? How do I load a Java Class that is not compiled?


回答1:


How do I load a Java Class that is not compiled?

You need to compile it first. This can be done programmatically with the javax.tools API. This only requires the JDK being installed at the local machine on top of JRE.

Here's a basic kickoff example (leaving obvious exception handling aside):

// Prepare source somehow.
String source = "package test; public class Test { static { System.out.println(\"hello\"); } public Test() { System.out.println(\"world\"); } }";

// Save source in .java file.
File root = new File("/java"); // On Windows running on C:\, this is C:\java.
File sourceFile = new File(root, "test/Test.java");
sourceFile.getParentFile().mkdirs();
Files.write(sourceFile.toPath(), source.getBytes(StandardCharsets.UTF_8));

// Compile source file.
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
compiler.run(null, null, null, sourceFile.getPath());

// Load and instantiate compiled class.
URLClassLoader classLoader = URLClassLoader.newInstance(new URL[] { root.toURI().toURL() });
Class<?> cls = Class.forName("test.Test", true, classLoader); // Should print "hello".
Object instance = cls.newInstance(); // Should print "world".
System.out.println(instance); // Should print "test.Test@hashcode".

Which yields like

hello
world
test.Test@ab853b

Further use would be more easy if those classes implements a certain interface which is already in the classpath.

SomeInterface instance = (SomeInterface) cls.newInstance();

Otherwise you need to involve the Reflection API to access and invoke the (unknown) methods/fields.


That said and unrelated to the actual problem:

properties.load(new FileInputStream(new File("ClassName.properties")));

Letting java.io.File rely on current working directory is recipe for portability trouble. Don't do that. Put that file in classpath and use ClassLoader#getResourceAsStream() with a classpath-relative path.

properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("ClassName.properties"));



回答2:


In the same vein as BalusC's answer, but a bit more automatic wrapper is here in this piece of code from my kilim distribution. https://github.com/kilim/kilim/blob/master/src/kilim/tools/Javac.java

It takes a list of strings containing Java source, extracts the package and public class/interface names and creates the corresponding directory/file hierarchy in a tmp directory. It then runs the java compiler on it, and returns a list of name,classfile pairs (the ClassInfo structure).

Help yourself to the code. It is MIT licensed.




回答3:


Your commented code is correct if you know that the class has a public no-arg constructor. You just have to cast the result, as the compiler can't know that the class will in fact implement IDynamicLoad. So:

   IDynamicLoad newClass = (IDynamicLoad) Class.forName(class_name).newInstance();

Of course the class has to be compiled and on the classpath for that to work.

If you are looking to dynamically compile a class from source code, that is a whole other kettle of fish.



来源:https://stackoverflow.com/questions/2946338/how-do-i-programmatically-compile-and-instantiate-a-java-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!