web-dev-qa-db-ja.com

Javaクラスをプログラムでコンパイルしてインスタンス化するにはどうすればよいですか?

クラス名はプロパティファイルに保存されています。クラスストアがIDynamicLoadを実装することを知っています。クラスを動的にインスタンス化するにはどうすればよいですか?

今私は持っています

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

NewInstanceはコンパイルされた.classファイルのみをロードしますか? Javaコンパイルされていないクラスをロードするにはどうすればよいですか?

64
unj2

Javaコンパイルされていないクラスをロードするにはどうすればよいですか?

最初にコンパイルする必要があります。これは _javax.tools_ API を使用してプログラムで実行できます。これには、JREの上のローカルマシンに [〜#〜] jdk [〜#〜] のみをインストールする必要があります。

基本的なキックオフの例は次のとおりです(明らかな例外処理は別として)。

_// 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".
_

のようになります

_hello
world
test.Test@ab853b
_

これらのクラスimplementsがすでにクラスパスにある特定のインターフェイスである場合、さらに使用するのはより簡単です。

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

それ以外の場合は、(不明な)メソッド/フィールドにアクセスして呼び出すために Reflection API を含める必要があります。


とはいえ、実際の問題とは無関係です。

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

_Java.io.File_を現在の作業ディレクトリに依存させることは、移植性の問題のレシピです。しないでください。そのファイルをクラスパスに配置し、クラスパス相対パスで ClassLoader#getResourceAsStream() を使用します。

_properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("ClassName.properties"));
_
132
BalusC

BalusCの答えと同じように、私のキリムディストリビューションのこのコードには、もう少し自動化されたラッパーがあります。 https://github.com/kilim/kilim/blob/master/src/kilim/tools/Javac.Java

Javaソースを含む文字列のリストを取得し、パッケージとパブリッククラス/インターフェイス名を抽出し、tmpディレクトリに対応するディレクトリ/ファイル階層を作成します。その後、Javaコンパイラで、名前とクラスファイルのペア(ClassInfo構造体)のリストを返します。

コードを手伝ってください。 MITライセンス。

6

クラスに引数なしのパブリックコンストラクターがあることがわかっている場合、コメント付きのコードは正しいです。クラスが実際にIDynamicLoadを実装することをコンパイラが認識できないため、結果をキャストする必要があります。そう:

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

もちろん、クラスをコンパイルし、クラスパス上で動作させる必要があります。

ソースコードからクラスを動的にコンパイルする場合、それはまったく別のやかんです。

5
Yishai