суббота, 15 июля 2017 г.

Генерирование классов во время выполнения

dynamic

Иногда требуется создать класс с определенным поведением во время работы программы. К сожалению, Java не обладает динамизмом, присущем таким языками как Python, Javascript, Groovy. Но все же, возможность такая существует, и есть несколько способов это сделать. Например, сгенерировать напрямую байткод. Но такой способ, довольно сложен. Мы же, в данной статье, напишем исходный код и затем скомпилируем его.

Для компиляции в стандартной библиотеке Java, существует класс JavaCompiler. Следующий метод производит компиляцию исходного кода и возвращает путь до директории со компилированными классами:

Метод getTask последним параметром принимает, коллекцию объектов. Эти объекты являются представлением некоторого источника исходного кода. Это может быть либо файл, либо как в нашем примере, строка. Готового класса, для строки не нашлось, поэтому приходится создать его самостоятельно:

Чтобы загрузить полученный класс, нам потребуется ClassLoader. Создать его можно следующим образом:

Теперь имея ClassLoader, мы можем получить объект типа Class и создать экземпляр нужного класса. Но, т.к. класс во время компиляции не был известен, соотвественно, напрямую вызывать какие либо методы из него мы не можем (кроме тех, что объявлены в классе java.lang.Object). Поэтому нужно использовать механизм Reflection.

Следующий код, создает два класса ru.izebit.Person, и вызывает у них метод say:

Заметьте, что классы имеют одинаковые названия, с точностью до пакета. Но т.к. используются разные ClassLoader'ы, то это вполне допустимо и никакого конфликта нет. Результат работы, как вы уже догадались будет следующим:

i am happy 🤠
i am tired 😫 
Весь код целиком можно посмотреть на github.