@LookAtIos

Как запустить 10000 классов в java?

Интересное задание по java: запустить в проекте 1000 классов, которые выводят номер класса. Просто копировать и компилировать, наверное, не то. Какие лучшие способы предложите для решения задачи?
  • Вопрос задан
  • 246 просмотров
Решения вопроса 1
sergey-gornostaev
@sergey-gornostaev Куратор тега Java
Седой и строгий
Прежде всего нужно заметить, что запустить класс нельзя, его можно только объявить, а запускать можно методы экземпляров этого класса. Почему я и подумал, что вопрос неправильно сформулирован.

Но если уж вопрос именно об объявлении новых классов в рантайме, то это называется кодогенерацией. И, во-первых, рефлексия в этом бессильна, а во-вторых, это очень сложная тема, озадачить которой начинающего программиста мог только очень мерзкий тролль.

Генерировать можно как java-код, так и байткод. Первый способ использует популярная библиотека Lombok. Для него понадобиться механизм работы с AST и механизм взаимодействия с компилятором. Oracle JDK и Open JDK предоставляют Compiler API, реализующий оба механизма. Кроме того, есть и другие инструменты для работы с AST, например Eclipse JDT. Я покажу применение пакета com.sun.tools.javac из Compiler API.
Сноска
До Java 9 пакет com.sun.tools.javac был упакован в tools.jar, поставляемый вместе с JDK. Начиная с 9-ки API компилятора вынесли в модуль jdk.compiler, не экспортирующие свои пакеты. Теперь можно не указывать путь до tools.jar в classpath, но нужно добавлять экспорты. В остальном ничего не изменилось.
import java.lang.reflect.Method;
import java.net.URI;
import java.util.ArrayList;
 
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileObject;
import javax.tools.SimpleJavaFileObject;
import javax.tools.ToolProvider;

import com.sun.tools.javac.code.Flags;
import com.sun.tools.javac.code.Type;
import com.sun.tools.javac.file.JavacFileManager;
import com.sun.tools.javac.model.JavacElements;
import com.sun.tools.javac.processing.JavacProcessingEnvironment;
import com.sun.tools.javac.tree.JCTree;
import com.sun.tools.javac.tree.TreeMaker;
import com.sun.tools.javac.util.Context;
import com.sun.tools.javac.util.List;
import com.sun.tools.javac.util.Name;


/**
 * Класс эмулирующий для компилятора файлы исходного кода
 * и позволяющий компилировать код прямо из памяти
 */
class JavaSourceFromString extends SimpleJavaFileObject {
    private final String code;

    public JavaSourceFromString(String name, String code) {
        super(URI.create("string:///" + name.replace('.','/') + Kind.SOURCE.extension), Kind.SOURCE);
        this.code = code;
    }

    @Override
    public CharSequence getCharContent(boolean ignoreEncodingErrors) {
        return code;
    }
}
 
public class CompilerDemo {
    private static final String BASE_NAME = "DynamicHello";
    private static final ClassLoader classLoader = ToolProvider.getSystemToolClassLoader();
    private static final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

    /**
     * Метод формирующий абстрактное синтаксическое дерево
     * и преобразующий его в исходный код
     */
    private static String generateSource(String className) {
        Context ctx = new Context();
        JavacFileManager.preRegister(ctx);
        TreeMaker treeMaker = TreeMaker.instance(ctx);
        JavacElements elements = JavacElements.instance(ctx);

        JCTree.JCModifiers modifiers = treeMaker.Modifiers(Flags.PUBLIC);

        // Тело генерируемого метода
        // выводящее имя класса
        JCTree.JCBlock methodBody = treeMaker.Block(0, List.of(
            treeMaker.Exec(
                treeMaker.Apply(
                    List.<JCTree.JCExpression>nil(),
                    treeMaker.Select(
                        treeMaker.Select(
                            treeMaker.Ident(
                                elements.getName("System")
                            ),
                            elements.getName("out")
                        ),
                        elements.getName("println")
                    ),
                    List.<JCTree.JCExpression>of(
                        treeMaker.Binary(
                            JCTree.Tag.PLUS,
                            treeMaker.Literal("I am "),
                            treeMaker.Apply(
                                List.<JCTree.JCExpression>nil(),
                                treeMaker.Select(
                                    treeMaker.Apply(
                                        List.<JCTree.JCExpression>nil(),
                                        treeMaker.Select(
                                            treeMaker.Ident(
                                                elements.getName("this")
                                            ),
                                            elements.getName("getClass")
                                        ),
                                        List.<JCTree.JCExpression>nil()
                                    ),
                                    elements.getName("getName")
                                ),
                                List.<JCTree.JCExpression>nil()
                            )
                        )
                    )
                )
            )
        ));

        // Определение генерируемого метода
        JCTree.JCMethodDecl method = treeMaker.MethodDef(
            modifiers,
            elements.getName("introduceYourself"),
            treeMaker.Type(new Type.JCVoidType()),
            List.<JCTree.JCTypeParameter>nil(),
            List.<JCTree.JCVariableDecl>nil(),
            List.<JCTree.JCExpression>nil(),
            methodBody,
            null
        );

        // Определение генерируемого класса
        JCTree.JCClassDecl tree = treeMaker.ClassDef(
            modifiers,
            elements.getName(className),
            List.<JCTree.JCTypeParameter>nil(),
            null,
            List.<JCTree.JCExpression>nil(),
            List.of(method)
        );

        return tree.toString();
    }

    /**
     * Метод компилирующий исходный код
     */
    public static void compile(Iterable<? extends JavaFileObject> compilationUnits) {
        CompilationTask task = compiler.getTask(null, null, null, null, null, compilationUnits);
        task.call();    
    }


    /**
     * Метод запускающий сгенерированные классы с помощью рефлексии
     */    
    public static void loadAndRun(String className) throws ReflectiveOperationException {
        Class<?> cls = classLoader.loadClass(className);
        
        Method method = cls.getDeclaredMethod("introduceYourself");
        method.invoke(cls.newInstance());    
    }

    public static void main(String[] args) throws Exception {
        java.util.List<JavaFileObject> sources = new ArrayList<>();

        // Генерируем исходный код десятка классов
        for (int x = 0; x < 10; x++) {
            String className = BASE_NAME + x;
            sources.add(new JavaSourceFromString(className, generateSource(className)));
        }

        // Компилируем сгенерированный код
        compile(sources);
        
        // Запускаем скомпилированные классы
        for (int x = 0; x < 10; x++) {
            loadAndRun(BASE_NAME + x);
        }
    }
}

Конечно, можно было не заморачиваться с AST, просто засунуть код в строковый литерал, подставлять в этот литерал разные имена и подсовывать компилятору. Но это не так интересно!

Продолжение в первом комментарии, а то я в лимит символов не укладываюсь.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
iLLuzor
@iLLuzor
Java, Kotlin, Android Developer
Самое простое - циклы.
Посложней - циклы и потоки.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы
ЛАНИТ Москва
от 180 000 руб.
YLab Москва
от 100 000 до 160 000 руб.
от 70 000 до 135 000 руб.
22 окт. 2019, в 08:58
15000 руб./за проект
22 окт. 2019, в 08:56
500 руб./в час
22 окт. 2019, в 08:46
150000 руб./за проект