Come faccio a compilare e istanziare a livello di codice una class Java?

Ho il nome della class memorizzato in un file di proprietà. So che l’archivio delle classi implementerà IDynamicLoad. Come posso istanziare la class in modo dinamico?

Proprio ora che ho

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(); 

La newstance carica solo file .class compilati? Come carico una class Java che non è compilata?

Come carico una class Java che non è compilata?

Devi prima compilarlo. Questo può essere fatto a livello di javax.tools con l’ API javax.tools . Ciò richiede solo l’installazione di JDK sulla macchina locale sopra JRE.

Ecco un esempio base di kickoff (lasciando da parte le evidenti eccezioni):

 // 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 "[email protected]". 

Quale rendimento

 hello world [email protected] 

Un ulteriore utilizzo sarebbe più semplice se tali classi implements una determinata interfaccia che è già nel classpath.

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

Altrimenti è necessario coinvolgere l’ API di Reflection per accedere e richiamare i metodi / campi (sconosciuti).


Detto questo e non correlato al problema attuale:

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

Lasciando java.io.File fare affidamento sulla directory di lavoro corrente è una ricetta per problemi di portabilità. Non farlo. Metti quel file in classpath e usa ClassLoader#getResourceAsStream() con un percorso relativo al classpath.

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

Allo stesso modo della risposta di BalusC, ma un wrapper un po ‘più automatico è qui in questo pezzo di codice dalla mia distribuzione di kilim. https://github.com/kilim/kilim/blob/master/src/kilim/tools/Javac.java

Prende un elenco di stringhe contenenti l’origine Java, estrae il pacchetto e i nomi di classi / interfacce pubbliche e crea la corrispondente directory / gerarchia di file in una directory tmp. Quindi esegue il compilatore java su di esso e restituisce un elenco di nomi, coppie di file di classi (la struttura ClassInfo).

Aiuta te stesso al codice. È autorizzato dal MIT.

Il tuo codice commentato è corretto se sai che la class ha un costruttore no-arg pubblico. Devi solo IDynamicLoad il risultato, in quanto il compilatore non può sapere che la class implementerà effettivamente IDynamicLoad . Così:

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

Ovviamente la class deve essere compilata e sul classpath per farlo funzionare.

Se stai cercando di compilare dynamicmente una class dal codice sorgente, si tratta di un intero altro insieme di pesci.