Перейти к основному содержанию

Примеры

note

Чтобы запустить примеры, необходимо клонировать ActiveJ с GitHub:

git clone https://github.com/activej/activej

И импортируйте его как проект Maven. Посмотрите тег v5.0-beta2. Перед запуском примеров выполните сборку проекта. Эти примеры расположены по адресу activej/examples/core/codegen.

Выражения байткода#

Давайте создадим простой метод sayHello() , который печатает "Hello world!". Сначала мы определим простой Пример интерфейс, который имеет единственный sayHello() метод:

public interface Greeter {  void sayHello();}

Теперь мы можем перейти к описанию поведения подкласса Пример . Для этого мы будем использовать класс ClassBuilder .

Class<Greeter> greeterClass = ClassBuilder.create(Greeter.class)    .withMethod("sayHello",        call(staticField(System.class, "out"), "println", value("Hello world")))    .defineClass(CLASS_LOADER);

Чтобы инстанцировать описанный класс, просто используйте newInstance():

Greeter greeter = greeterClass.getDeclaredConstructor().newInstance();greeter.sayHello();
See full example on GitHub## Динамическое создание классов В этом примере мы динамически создадим класс, реализующий интерфейс. Итак, давайте сначала создадим простой интерфейс **Person** :
@SuppressWarnings("unused")public interface Person extends Comparable<Person> {  void setIdAndName(int id, String name);
  int getId();
  String getName();
  int hashOfPojo(ExamplePojo personPojo);
  int hash();
  @Override  int compareTo(@NotNull Person o);
  @Override  String toString();
  @Override  boolean equals(Object obj);}

Перейдите к построению класса, реализующего интерфейс Person :

// declare fields// setter for both fields - a sequence of actions// compareTo, equals, hashCode and toString methods implementations follow the standard conventionClass<Person> personClass = ClassBuilder.create(Person.class)    // declare fields    .withField("id", int.class)    .withField("name", String.class)
    // setter for both fields - a sequence of actions    .withMethod("setIdAndName", sequence(        set(property(self(), "id"), arg(0)),        set(property(self(), "name"), arg(1))))    .withMethod("getId", property(self(), "id"))    .withMethod("getName", property(self(), "name"))
    // compareTo, equals, hashCode and toString methods implementations follow the standard convention    .withMethod("int compareTo(Person)", compareToImpl("id", "name"))    .withMethod("equals", equalsImpl("id", "name"))    .withMethod("hashOfPojo", hash(property(arg(0), "id"), property(arg(0), "name")))    .withMethod("hash", hash(property(self(), "id"), property(self(), "name")))    .withMethod("toString", ExpressionToString.create()        .withQuotes("{", "}", ", ")        .with("id: ", property(self(), "id"))        .with("name: ", property(self(), "name")))    .defineClass(CLASS_LOADER);

Теперь мы можем протестировать наши динамически созданные классы:

// Instantiate two objects of dynamically defined classPerson jack = personClass.getDeclaredConstructor().newInstance();Person martha = personClass.getDeclaredConstructor().newInstance();
jack.setIdAndName(5, "Jack");martha.setIdAndName(jack.getId() * 2, "Martha");
System.out.println("First person: " + jack);System.out.println("Second person: " + martha);
System.out.println("jack.equals(martha) ? : " + jack.equals(martha));
See full example on GitHub## Пример калькулятора В этом примере мы создадим калькулятор, который будет анализировать строку уравнений в AST. Затем генерируется оптимизированный класс для вычисления выражения. Сначала создайте синтаксический анализатор, который возвращает AST выражения:
private static final Parser<Expression> EXPRESSION = new OperatorTable<Expression>()    .infixl(DELIMITERS.token("+").retn(Expressions::add), 10)    .infixl(DELIMITERS.token("-").retn(Expressions::sub), 10)    .infixl(DELIMITERS.token("*").retn(Expressions::mul), 20)    .infixl(DELIMITERS.token("/").retn(Expressions::div), 20)    .infixl(DELIMITERS.token("%").retn(Expressions::rem), 20)    .prefix(DELIMITERS.token("-").retn(Expressions::neg), 30)    .infixr(DELIMITERS.token("^").retn((left, right) -> Expressions.staticCall(Math.class, "pow", left, right)), 40)    .build(ATOM);

Затем создайте ClassBuilder , который описывает класс, который будет создан. Он будет реализовывать интерфейс DoubleUnaryOperator и будет иметь метод applyAsDouble . Давайте создадим соответствующий конструктор:

public static Class<DoubleUnaryOperator> compile(String expression) {  return ClassBuilder.create(DoubleUnaryOperator.class)      .withMethod("applyAsDouble", PARSER.parse(expression))      .defineClass(CLASS_LOADER);}

Метод будет иметь параметр var1 для неизвестного x:

private static final Parser<Expression> UNKNOWN = DELIMITERS.token("x").retn(Expressions.arg(0));

В результате ActiveJ Codegen сгенерирует байткод следующего класса:

public final class Class1 implements DoubleUnaryOperator {    public Class1() {    }
    public double applyAsDouble(double var1) {        return (2.0D 2.0D * 2.0D) * -var1 5.0D 1024.0D / (100.0D 58.0D) * 50.0D * 37.0D - 100.0D 2.0D * Math.pow(var1, 2.0D) .0D;    }}

Теперь давайте обработаем написанный вручную код и динамически сгенерированную оценку экземпляра:

public static void main(String[] args) throws Exception {  double x = -1;
  // manual code, super fast  System.out.println(((2.0 + 2.0 * 2.0) * -x) + 5.0 + 1024.0 / (100.0 + 58.0) * 50.0 * 37.0 - 100.0 + 2.0 * Math.pow(x, 2.0) % 3.0);
  DoubleUnaryOperator instance = compile("((2 + 2 * 2) * -x) + 5 + 1024 / (100 + 58) * 50 * 37 - 100 + 2 * x ^ 2 % 3").getDeclaredConstructor().newInstance();
  // generated instance evaluation, literally equivalent to manual code (with a method call around it), except it was dynamically generated  System.out.println(instance.applyAsDouble(x));}

Мы также провели сравнительные тесты для этого выражения, чтобы сравнить производительность:

Benchmark Mode Cnt Score Error UnitsCalculatorBenchmark.generated avgt 10 115.882 ± 1.082 ns/opCalculatorBenchmark.manual avgt 10 115.222 ± 1.600 ns/op

Примеры источников можно найти на GitHub