Подтвердить что ты не робот

Как программно компилировать и создавать экземпляр класса 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, который не скомпилирован?

4b9b3361

Ответ 1

Как загрузить класс Java, который не скомпилирован?

Сначала вам нужно скомпилировать его. Это можно сделать программно с помощью javax.tools API. Для этого требуется только JDK, установленный на локальной машине поверх JRE.

Здесь приведен пример базового запуска (оставление очевидной обработки исключений):

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

Что дает, например,

hello
world
[email protected]

Дальнейшее использование было бы более легким, если бы эти классы implements имели определенный интерфейс, который уже находится в пути к классам.

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

В противном случае вам понадобится API отражения для доступа и вызова (неизвестных) методов/полей.


Это сказало и не связано с реальной проблемой:

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

Предоставление java.io.File полагаться на текущий рабочий каталог - это рецепт проблемы с переносимостью. Не делай этого. Поместите этот файл в путь к классам и используйте ClassLoader#getResourceAsStream() с относительным путем classpath.

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

Ответ 2

В том же духе, что и ответ BalusC, но немного более автоматическая обертка находится здесь в этом фрагменте кода из моего дистрибутива. https://github.com/kilim/kilim/blob/master/src/kilim/tools/Javac.java

Он принимает список строк, содержащих источник Java, извлекает имена пакетов и общего класса/интерфейса и создает соответствующую директорию/файловую иерархию в каталоге tmp. Затем он запускает java-компилятор и возвращает список имен, классов класса (структура ClassInfo).

Помогите себе с кодом. Это лицензия MIT.

Ответ 3

Прокомментированный код верен, если вы знаете, что класс имеет открытый конструктор no-arg. Вам просто нужно указать результат, поскольку компилятор не может знать, что класс фактически реализует IDynamicLoad. Итак:

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

Разумеется, класс должен быть скомпилирован и в classpath для работы.

Если вы хотите динамически компилировать класс из исходного кода, это целый другой чайник рыбы.