Используйте JCodeModel для генерации метода toString()

Я пытаюсь сгенерировать Java Value Objects, используя com.sun.codemodel.JCodeModel,

Мне удалось сформировать hashcode() а также equals() методы, но я борюсь с toString();

Мне требуется следующее toString() реализация

return "ClassName [field1 = " + field1 + ", field2 = " + field2 ... ", fieldN = " + fieldN + "]";

Как мне создать JCodeModelJExpression который содержит JExpr.lit(field1.name()) соединенный с JExpr.ref(fieldVar.name())?

Все, что мне удалось сделать, это сгенерировать строковый литерал, напоминающий:-

return "ClassName [field1 =  field1 + field2 = field2 ... fieldN = + fieldN + ]";

Вот мой метод скелета:

final Map<String, JFieldVar> fields = jclass.fields();
final JMethod toString = jclass.method(JMod.PUBLIC, String.class, "toString");
final Set<String> excludes = new HashSet<String>(Arrays.asList(ruleFactory.getGenerationConfig().getToStringExcludes()));

final JBlock body = toString.body();

for (JFieldVar fieldVar : fields.values()) {
    if (excludes.contains(fieldVar.name()) || ((fieldVar.mods().getValue() & JMod.STATIC) == JMod.STATIC)) {
        continue;
    }

    ??????????????

}

body._return(?????????);

toString.annotate(Override.class);

1 ответ

Решение

Ключевым моментом здесь, вероятно, является то, что вы можете объединить несколько JExpression объекты с + оператор с помощью JExpression#plus метод.

Вот пример, который содержит определение простого примера класса и метод для генерации toString метод:

import java.util.Arrays;
import java.util.Collection;
import java.util.Map;

import com.sun.codemodel.CodeWriter;
import com.sun.codemodel.JBlock;
import com.sun.codemodel.JCodeModel;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JExpr;
import com.sun.codemodel.JExpression;
import com.sun.codemodel.JFieldVar;
import com.sun.codemodel.JMethod;
import com.sun.codemodel.JMod;
import com.sun.codemodel.writer.SingleStreamCodeWriter;

public class CodeModelToStringTest
{
    public static void main(String[] args) throws Exception
    {
        JCodeModel codeModel = new JCodeModel();
        JDefinedClass definedClass = codeModel._class("com.example.Example");

        definedClass.field(JMod.PUBLIC, String.class, "exampleString");
        definedClass.field(JMod.PROTECTED, int.class, "exampleInt");
        definedClass.field(JMod.PRIVATE, float.class, "exampleFloat");

        definedClass.field(JMod.PRIVATE, String.class, "excludedString");
        definedClass.field(JMod.STATIC, String.class, "staticString");

        createToStringMethod(definedClass, Arrays.asList("excludedString"));

        CodeWriter codeWriter = new SingleStreamCodeWriter(System.out);
        codeModel.build(codeWriter);
    }

    private static void createToStringMethod(
        JDefinedClass definedClass,
        Collection<String> excludedFieldNames)
    {
        Map<String, JFieldVar> fields = definedClass.fields();
        JMethod toString =
            definedClass.method(JMod.PUBLIC, String.class, "toString");
        toString.annotate(Override.class);

        JBlock body = toString.body();

        JExpression expression = JExpr.lit(definedClass.name() + " [");

        boolean first = true;
        for (JFieldVar fieldVar : fields.values())
        {
            if ((fieldVar.mods().getValue() & JMod.STATIC) == JMod.STATIC)
            {
                continue;
            }
            if (excludedFieldNames.contains(fieldVar.name()))
            {
                continue;
            }
            if (!first)
            {
                expression = expression.plus(JExpr.lit(", "));
            }
            expression = expression.plus(JExpr.lit(fieldVar.name()+" = "));
            expression = expression.plus(JExpr.ref(fieldVar.name()));
            first = false;
        }
        expression = expression.plus(JExpr.lit("]"));

        body._return(expression);


    }
}

Сгенерированный класс с toString метод показан здесь:

package com.example;


public class Example {

    public String exampleString;
    protected int exampleInt;
    private float exampleFloat;
    private String excludedString;
    static String staticString;

    @Override
    public String toString() {
        return ((((((((("Example ["+"exampleString = ")+ exampleString)+", ")+"exampleInt = ")+ exampleInt)+", ")+"exampleFloat = ")+ exampleFloat)+"]");
    }

}

Тот факт, что CodeModel вставляет ( скобки ) вокруг каждой двоичной операции код не выглядит так красиво. Но это понятно: в противном случае им придется учитывать приоритеты операторов, а использование и генерация кода, вероятно, будут гораздо сложнее.

Тем не менее, результат этого toString метод будет

Example [exampleString = null, exampleInt = 0, exampleFloat = 0.0]

что должно быть, что вы ожидали, основываясь на ваших примерах.

Другие вопросы по тегам