有没有办法将Java ArrayList对象复制到CodeModel生成的源代码中?

3
我是一位有用的助手,可以为您翻译文本。
我有一个已填充的ArrayList(非CodeModel)在使用CodeModel的代码生成类中,并且我想在生成的代码中使用它。是否有任何方法可以做到这一点?这有点“跨界”,因为生成的代码不引用或依赖于生成它的代码,但是我仍然希望使用ArrayList的值,而不必通过手动逐个复制遍历来实现...与字符串、数字等一样,肯定有一种方法可以做到这一点...?
我的示例代码如下:
private JMethod makeHeadersWrapper( String endPointName, ArrayList<BasicNameValuePair> headersList )
{

    JMethod wrapperMethod = definedClass.method( JMod.PUBLIC | JMod.STATIC, codeModel.ref( headersList.getClass() ).narrow( BasicNameValuePair.class ), StringUtils.formatName( "make" + endPointName + "Header", false, StringUtils.FormatType.UP_ENGLISH_WORDS ) );

    JVar headersListVar = wrapperMethod.body().decl( wrapperMethod.type(), "headersList", {X} ) );
    wrapperMethod.body()._return( headersListVar );

    return wrapperMethod;

}

在我的任务声明中,你会看到有一个空白的{X}字段,我正在尝试弄清楚如何编写它。话虽这么说,{X}应该引用makeHeadersWrapper签名中的'headersList'参数。是否有任何方法可以做到这一点,以引用变量的值作为{X}并将其作为codemodel JExpr(ession)?请帮我解决{X}!:P

真诚地感谢, Piotr


你的意思是生成的代码应该有这个包含所有值的ArrayList吗?然后你可以创建JVar并初始化它。但是仍然需要循环遍历它。 - Syam S
Syam S,这正是我想要的。我希望它能像使用JExpr.lit(somePrimitive)一样工作。我知道JVar是正确的方法,但是绕过循环肯定没有其他方法吗?我正在寻找一种按值传递的解决方案,在这种情况下,“值”指的是ArrayList的内容。谢谢! :) - Piotr
我认为没有像你想的那样的方法。JVar支持字面量和数组,但不支持ArrayList。即使使用JArray,您仍需要进行循环或方法链接。 :) - Syam S
1个回答

3
基本上我认为 JExpression 不支持 Lists。它确实有一个 JArray,但你仍然需要遍历 headersList。在 codemode-2.6 中,JExprJExpression 中没有一种方法可以迭代列表。因此,你不能省略迭代部分。因此标准做法是遍历 headersList 并添加初始化语句。
例如,假设你有一个 Namevalue pair:
public class BasicNameValuePair {
    private String name;
    private String value;

    public BasicNameValuePair(String name, String value) {
        this.name = name;
        this.value = value;
    }
    //getters & setters
}

并且像这样进行初始化

List<BasicNameValuePair> headerList = new ArrayList<BasicNameValuePair>();
headerList.add(new BasicNameValuePair("1", "1"));
headerList.add(new BasicNameValuePair("2", "2"));
headerList.add(new BasicNameValuePair("3", "3"));
headerList.add(new BasicNameValuePair("4", "4"));

然后,您可以像这样生成方法体:
private JMethod makeHeadersWrapper( String endPointName, List<BasicNameValuePair> headersList ) {

    JClass headerClass = codeModel.ref( headersList.getClass() ).narrow( BasicNameValuePair.class );
    JMethod wrapperMethod = definedClass.method( JMod.PUBLIC | JMod.STATIC, headerClass, ( "make" + endPointName + "Header") );

    JVar headersListVar = wrapperMethod.body().decl( wrapperMethod.type(), "headersList", JExpr._new(headerClass) );
    for(BasicNameValuePair nameValuePair : headersList) {
        wrapperMethod.body().add(headersListVar.invoke("add").arg(JExpr._new(nameValuePairClass).arg(nameValuePair.getName()).arg(nameValuePair.getValue())));
    }
    wrapperMethod.body()._return( headersListVar );

    return wrapperMethod;
}

这将生成代码。
public static ArrayList<BasicNameValuePair> makeTestHeader() {
    ArrayList<BasicNameValuePair> headersList = new ArrayList<BasicNameValuePair>();
    headersList.add(new BasicNameValuePair("1", "1"));
    headersList.add(new BasicNameValuePair("2", "2"));
    headersList.add(new BasicNameValuePair("3", "3"));
    headersList.add(new BasicNameValuePair("4", "4"));
    return headersList;
}

然而,ArrayList 有一个使用 {{ }} 的构造方法。所以你可以这样声明:

List<String> lst = new ArrayList<String>(){{ add("1"); add("2"); add("3"); add("4"); }};

JExpr有一个direct()方法,可以直接传递源代码。使用这两个方法,我们可以生成以下代码。我不建议使用这种方法,但它是可能的。

private JMethod makeHeadersWrapper( String endPointName, List<BasicNameValuePair> headersList ) {
    JMethod wrapperMethod = definedClass.method( JMod.PUBLIC | JMod.STATIC, codeModel.ref( headersList.getClass() ).narrow( BasicNameValuePair.class ), ( "make" + endPointName + "Header") );

    JVar headersListVar = wrapperMethod.body().decl( wrapperMethod.type(), "headersList", JExpr.direct(generateListConstructor(headersList)) );
    wrapperMethod.body()._return( headersListVar );

    return wrapperMethod;

}

private String generateListConstructor(List<BasicNameValuePair> headersList) {
    StringBuilder listEpr = new StringBuilder("new ArrayList<scope.BasicNameValuePair>(){{");
    for(BasicNameValuePair nameValuePair : headersList) {
        listEpr.append("add(new BasicNameValuePair(\"").append(nameValuePair.getName()).append("\", \"").append(nameValuePair.getValue()).append("\")); ");
    }
    return listEpr.append("}}").toString();
}

这将生成。
public static ArrayList<BasicNameValuePair> makeTestHeader() {
    ArrayList<BasicNameValuePair> headersList = (new ArrayList<scope.BasicNameValuePair>(){{add(new BasicNameValuePair("1", "1")); add(new BasicNameValuePair("2", "2")); add(new BasicNameValuePair("3", "3")); add(new BasicNameValuePair("4", "4")); }});
    return headersList;
}

这也是有效的Java代码。

这个答案可能不是你所寻找的完全答案,但它可能会给你一个进展的想法。希望这有所帮助。


嗨Syam S,感谢您的解决方案。不过您确定没有遍历和复制值的其他方法吗? :/ - Piotr
是的,几乎可以确定。我已经查看了codemodel-2.6.jar的源代码,但没有找到任何东西。除非有人有一个非常出色的解决方法.. ;) - Syam S
你可以将ArrayList序列化为字符串表示,然后生成该字符串的字面量到代码中。但那样做会很丑陋 -- 生成的代码不易读懂。 - Anna Dickinson
@Anna:好想法,但那会非常麻烦。我们可能需要使用Base64Coder,在使用之前我们还需要反序列化等操作,这会使我们的代码比通过循环实现更加复杂。此外,生成的代码将会变得难以理解。总之,不要迭代...思路不错 ;) - Syam S
是的,那不是一个“好”的方法来实现它,但您不必编写任何迭代代码。当然,在序列化方法操作时,迭代仍将在后台发生。但是,它确实回答了问题。 :-) - Anna Dickinson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接