是否有一个Java库可以生成Java源代码来创建对象?

我正在寻找一个API,它生成的源代码生成的对象与我传​​递给库的对象相同.

public static void main(String[] args) {
  int[] arr = new int[3];
  arr[0] = 3;
  arr[1] = 4;
  arr[2] = 5;

  // looking for this one
  MagicCode.generateCode(arr);
}

应该生成

  int[] arr = new int[3];
  arr[0] = 3;
  arr[1] = 4;
  arr[2] = 5;

要么

  int[] arr = new int[] { 3, 4, 5 };

所以我想传递一个Object并获取Java源代码,该代码将创建一个等于我的初始Object的对象.

这不仅适用于原始类型,也适用于我自己的对象:

public static void main(String[] args) {
  Condition condition = new Condition();
  condition.setNumber(2);

  // looking for this one
  MagicCode.generateCode(condition);
}

应该生成

  Condition c1 = new Condition();
  c1.setNumber(2);

作为一个String,然后可以粘贴到Java源文件.

编辑

我不想绕过编译器.

我即将重写一个未经过良好测试的组件.因此,大约需要编写1000个测试用例.功能基本上是输入/输出.我有1000个输入字符串,并希望在重写组件后测试它,它的行为完全相同.

因此,我希望每个对象都实现#toString(),以便创建Java源来创建自己.然后我可以传递我的1000个字符串并让当前实现编写测试用例以确保组件的行为.

最佳答案 代码生成很有趣!你可以通过使用反射来实现你所需要的,遗憾的是,没有实现MagicCode.

您需要使用内省来读取什么类型的对象并根据它创建它.

您可以使用Eclipse JDT API创建类.

Generating a compilation unit

The easiest way to programmatically generate a compilation unit is to use IPackageFragment.createCompilationUnit. You specify the name and contents of the compilation unit. The compilation unit is created inside the package and the new ICompilationUnit is returned.

docs开始,有一个示例代码段.

所以你基本上会反省看什么类型的对象以及它们的字段和当前值是什么.然后你将使用这个API做一个相应的AST.您的示例可能如下所示.

public class CodeGenerator {

    public static void main(String[] args) throws IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Foo foobar = new Foo();

        Bar bar = new Bar();
        bar.setSomeValue(555d);
        foobar.setBar(bar);
        foobar.setPrimitiveDouble(23);
        foobar.setValue("Hello World!");

        CodeGenerator codeGenerator = new CodeGenerator();

        String generatedCode = codeGenerator.generateCode(foobar);

        System.out.println(generatedCode);

    }

    int counter = 0;

    private String createVariableName(String clazzName) {
        return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, clazzName + getCurrentCounter());
    }

    public String generateCode(AST ast, List statements, Object object) throws IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        String clazzName = object.getClass().getSimpleName();
        String variableName = createVariableName(clazzName);

        VariableDeclarationFragment newVariable = ast.newVariableDeclarationFragment();
        newVariable.setName(ast.newSimpleName(variableName)); // Or clazzName.toCamelCase()

        ClassInstanceCreation newInstance = ast.newClassInstanceCreation();
        newInstance.setType(ast.newSimpleType(ast.newSimpleName(clazzName)));
        newVariable.setInitializer(newInstance);

        VariableDeclarationStatement newObjectStatement = ast.newVariableDeclarationStatement(newVariable);
        newObjectStatement.setType(ast.newSimpleType(ast.newSimpleName(clazzName)));

        statements.add(newObjectStatement);

        BeanInfo beanInfo = Introspector.getBeanInfo(object.getClass());
        for (PropertyDescriptor propertyDesc : beanInfo.getPropertyDescriptors()) {
            String propertyName = propertyDesc.getName();

            if (!shouldIgnore(propertyName)) {

                MethodInvocation setterInvocation = ast.newMethodInvocation();

                SimpleName setterName = ast.newSimpleName(propertyDesc.getWriteMethod().getName());
                setterInvocation.setName(setterName);

                Object invoked = propertyDesc.getReadMethod().invoke(object);

                if (invoked == null) {
                    continue;
                }

                if (Primitives.isWrapperType(invoked.getClass())) {
                    if (Number.class.isAssignableFrom(invoked.getClass())) {
                        setterInvocation.arguments().add(ast.newNumberLiteral(invoked.toString()));
                    }

                    // TODO: Booleans
                } else {

                    if (invoked instanceof String) {
                        StringLiteral newStringLiteral = ast.newStringLiteral();
                        newStringLiteral.setLiteralValue(invoked.toString());
                        setterInvocation.arguments().add(newStringLiteral);
                    } else {

                        String newObjectVariable = generateCode(ast, statements, invoked);
                        SimpleName newSimpleName = ast.newSimpleName(newObjectVariable);
                        setterInvocation.arguments().add(newSimpleName);
                    }

                }

                SimpleName newSimpleName = ast.newSimpleName(variableName);
                setterInvocation.setExpression(newSimpleName);

                ExpressionStatement setterStatement = ast.newExpressionStatement(setterInvocation);

                statements.add(setterStatement);

            }

        }

        return variableName;
    }

    public String generateCode(Object object) throws IntrospectionException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        resetCounter();

        AST ast = AST.newAST(AST.JLS3);
        Block block = ast.newBlock();

        generateCode(ast, block.statements(), object);

        return block.toString();

    }

    private int getCurrentCounter() {
        return counter++;
    }

    private void resetCounter() {
        counter = 0;
    }

    private boolean shouldIgnore(String propertyName) {
        return "class".equals(propertyName);
    }
}

我使用的依赖项:

    <dependency>
        <groupId>org.eclipse.tycho</groupId>
        <artifactId>org.eclipse.jdt.core</artifactId>
        <version>3.9.1.v20130905-0837</version>
    </dependency>
    <dependency>
        <groupId>org.eclipse.core</groupId>
        <artifactId>runtime</artifactId>
        <version>3.9.100-v20131218-1515</version>
    </dependency>
    <dependency>
        <groupId>org.eclipse.birt.runtime</groupId>
        <artifactId>org.eclipse.core.resources</artifactId>
        <version>3.8.101.v20130717-0806</version>
    </dependency>
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>18.0</version>
    </dependency>

这是输出的样子:

  Foo foo0=new Foo();
  Bar bar1=new Bar();
  bar1.setSomeValue(555.0);
  foo0.setBar(bar1);
  foo0.setPrimitiveDouble(23.0);
  foo0.setValue("Hello World!");

这是Foo和Bar类声明:

public class Bar {

private double someValue;

public double getSomeValue() {
    return someValue;
}

public void setSomeValue(double someValue) {
    this.someValue = someValue;
}

}

public class Foo {

private String value;
private double primitiveDouble;
private Bar bar;

public Bar getBar() {
    return bar;
}

public double getPrimitiveDouble() {
    return primitiveDouble;
}

public String getValue() {
    return value;
}

public void setBar(Bar bar) {
    this.bar = bar;
}

public void setPrimitiveDouble(double primitiveDouble) {
    this.primitiveDouble = primitiveDouble;
}

public void setValue(String value) {
        this.value = value;
    }
}

我根据要求将其添加到github repository.

点赞