views:

181

answers:

2

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?

+3  A: 

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.

Yishai
+1  A: 

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\"); } }";

// 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");
Writer writer = new FileWriter(sourceFile);
writer.write(source);
writer.close();

// 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);
Object instance = cls.newInstance(); // Should print "hello".
System.out.println(instance); // Should print "test.Test@hashcode".   

Which yields like

hello
test.Test@ab853b

Further use would be more easy if those classes implements a certian 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 touble. 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"));
BalusC
Just an off topic question. I get a null when I load the properties your way but I get the properties when I do it Foo.class.getResourceAsStream()? Could you help me understand your code? Thank you.
kunjaan
That properties file is apparently placed in the same package as `Foo` class. As said, you need to specify a classpath-relative path, e.g. `com/example/filename.properties`. But if you can guarantee that the properties file is always in the same package as `Foo` class, then `Class#getResourceAsStream()` is also okay. You'll only miss the ability to externalize the propertiesfile outside the application so that it can be modified without modifying/repackaging the application.
BalusC