JavaPoet 的使用

一、介绍

JavaPoet是一个用于生成. Java源文件的Java API。

gitHub:https://github.com/square/javapoet

文档地址:https://square.github.io/javapoet/1.x/javapoet/

在进行注释处理或与元数据文件(例如,数据库模式、协议格式)交互时,源文件生成非常有用。通过生成代码,您可以消除编写样板文件的需要,同时为元数据保留一个真实的源。

MethodSpec:

生成的构造函数或方法声明。

TypeSpec:

生成的类、接口或枚举声明。

JavaFile:

包含一个顶层类的Java文件。

1、例子:自动生成一个helloWorld 的main 方法

   public static void main(String[] args) throws IOException {
        //生成方法
        MethodSpec main =   MethodSpec.methodBuilder("main")
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .returns(void.class)
                .addParameter(String[].class, "args")
                .addStatement("$T.out.println($S)",System.class,"Hello, JavaPoet!").build();

        TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
                .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                .addMethod(main)
                .build();

        JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
                .build();
        javaFile.writeTo(System.out);
    }

二、代码与流程控制

$L发出一个没有转义的文字值。文字的参数可以是字符串、原语、类型声明、注释,甚至是其他代码块。

$N发出一个名称,在必要时使用名称避免冲突。名称的参数可以是字符串(实际上是任何字符序列)、参数、字段、方法和类型。

$S将值转义为字符串,用双引号括起来,并发出该值。例如,6" sandwich发出"6" sandwich"。

$T发出类型引用。如果可能,将导入类型。类型的参数可以是类、*类型镜像和元素。

$$发出美元符号。

$W根据其在行的位置发出空格或换行。这更喜欢将行包装在100列之前。

$Z充当一个零宽度的空间。这更喜欢将行包装在100列之前。

$>增加缩进级别。

$<降低缩进级别。

$[开始一个语句。对于多行语句,第一行之后的每一行都是双缩进的。

$]结束语句

JavaPoet 的大多数API 使用普通的旧的不可变Java对象。还有构建器、方法链接和varargs使API更加友好。JavaPoet 为类和接口(TypeSpec)、字段(FieldSpec)、方法和狗仔器(MethodSpec)、参数(ParameterSpec)和注释(AnnotationSpec)提供模型。

但是方法和构造函数主体没有建模。没有表达式类、语句类的相关树接节点,相反JavaPoet对代码块使用字符串

private static void testFor() throws IOException {
        MethodSpec main =  MethodSpec.methodBuilder("main").addCode("int total=0;\n for(int i=0;i<10;i++){ \n" +
                "   total += i;\n}\n").build();

        TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
                .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                .addMethod(main)
                .build();

        JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
                .build();
        javaFile.writeTo(System.out);
    }	

手工分号、换行和缩进都很繁琐,因此JavaPoet提供了api来简化这些操作。addStatement()负责分号和换行,beginControlFlow + endControlFlow()用户大括号、换行和缩进

 private static void testFor1() throws IOException {
        MethodSpec main =  MethodSpec.methodBuilder("main").addStatement("int total = 00")
                .beginControlFlow("for(int i = 0;i < 10; i++")
                .addStatement("total += i")
                .endControlFlow().build();

        TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
                .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                .addMethod(main)
                .build();

        JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
                .build();
        javaFile.writeTo(System.out);
    }

一些控制流语句,例如if/else,可以有无限的控制流可能性。您可以使用nextControlFlow()处理这些选项:

    private static void testFor1() throws IOException {
        MethodSpec main = MethodSpec.methodBuilder("main")
                .addStatement("long now = $T.currentTimeMillis()", System.class)
                .beginControlFlow("if ($T.currentTimeMillis() < now)", System.class)
                .addStatement("$T.out.println($S)", System.class, "Time travelling, woo hoo!")
                .nextControlFlow("else if ($T.currentTimeMillis() == now)", System.class)
                .addStatement("$T.out.println($S)", System.class, "Time stood still!")
                .nextControlFlow("else")
                .addStatement("$T.out.println($S)", System.class, "Ok, time still moving forward")
                .endControlFlow()
                .build();
        TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
                .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                .addMethod(main)
                .build();

        JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
                .build();
        javaFile.writeTo(System.out);
    }

使用try/catch捕捉异常也是nextControlFlow()的一个用例:

 private static void testFor2() throws IOException {
        MethodSpec main = MethodSpec.methodBuilder("main")
                .beginControlFlow("try")
                .addStatement("throw new Exception($S)", "Failed")
                .nextControlFlow("catch ($T e)", Exception.class)
                .addStatement("throw new $T(e)", RuntimeException.class)
                .endControlFlow()
                .build();
        TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
                .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                .addMethod(main)
                .build();

        JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
                .build();
        javaFile.writeTo(System.out);
    }

$L for Literals

调用beginControlFlow()和addStatement中的字符串连接会分散注意力。太多的运营商。为了解决这个问题,JavaPoet提供了一种语法,这种语法受到String.format()的启发,但又不兼容。它接受$L在输出中发出一个文字值。这就像Formatter的%s:

 private static void testFor3() throws IOException {
        TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
                .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                .addMethod(computeRange("main", 0, 10, "*"))
                .build();

        JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
                .build();
        javaFile.writeTo(System.out);
    }


    private static MethodSpec computeRange(String name, int from, int to, String op) {
        return MethodSpec.methodBuilder(name)
                .returns(int.class)
                .addStatement("int result = 0")
                .beginControlFlow("for (int i = $L; i < $L; i++)", from, to)
                .addStatement("result = result $L i", op)
                .endControlFlow()
                .addStatement("return result")
                .build();
    }

文本直接发送到输出代码,没有转义。文字的参数可以是字符串、原语和下面描述的一些JavaPoet类型。

$S for Strings

当发出包含字符串文本的代码时,我们可以使用$S来发出一个字符串,并使用引号括起来并转义。

  private static void testFor4() throws IOException {
        TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
                .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                .addMethod(whatsMyName("hello"))
                .build();

        JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
                .build();
        javaFile.writeTo(System.out);
    }

    private static MethodSpec whatsMyName(String name) {
        return MethodSpec.methodBuilder(name)
                .returns(String.class)
                .addStatement("return $S", name)
                .build();
    }

$T for Types

我们Java程序员喜欢我们的类型:它们使我们的代码更容易理解。JavaPoet也加入了。它对类型有丰富的内置支持,包括自动生成导入语句。只需使用$T引用类型:

 private static void testForType() throws IOException {
        MethodSpec today = MethodSpec.methodBuilder("today")
                .returns(Date.class)
                .addStatement("return new $T()", Date.class)
                .build();

        TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
                .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                .addMethod(today)
                .build();

        JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
                .build();

        javaFile.writeTo(System.out);
    }

我们传递Date.class来引用一个恰好在生成代码时可用的类。其实并不需要这样。下面是一个类似的例子,但是这个例子引用了一个不存在的类:

ClassName hoverboard = ClassName.get("com.mattel", "Hoverboard");

MethodSpec today = MethodSpec.methodBuilder("tomorrow")
    .returns(hoverboard)
    .addStatement("return new $T()", hoverboard)
    .build();

ClassName类型非常重要,在使用JavaPoet时经常需要它。它可以标识任何已声明的类。声明类型只是Java丰富类型系统的开始:我们还有数组、参数化类型、通配符类型和类型变量。JavaPoet有用于构建每个类的类:

private static void testForType1() throws IOException {
     ClassName hoverboard = ClassName.get("com.mattel", "Hoverboard");
     ClassName list = ClassName.get("java.util", "List");
     ClassName arrayList = ClassName.get("java.util", "ArrayList");
     TypeName listOfHoverboards = ParameterizedTypeName.get(list, hoverboard);

     MethodSpec beyond = MethodSpec.methodBuilder("beyond")
             .returns(listOfHoverboards)
             .addStatement("$T result = new $T<>()", listOfHoverboards, arrayList)
             .addStatement("result.add(new $T())", hoverboard)
             .addStatement("result.add(new $T())", hoverboard)
             .addStatement("result.add(new $T())", hoverboard)
             .addStatement("return result")
             .build();

     TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
             .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
             .addMethod(beyond)
             .build();

     JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
             .build();

     javaFile.writeTo(System.out);
    }

Import static

JavaPoet支持导入静态。它通过显式收集类型成员名来实现这一点。让我们用一些静态的糖来增强前面的例子:

private static void testForType2() throws IOException {
        ClassName className = ClassName.get("com.union.xiaobo", "TestForTyype");
        ClassName list = ClassName.get("java.util","List");
        ClassName arrayList = ClassName.get("java.util","ArrayList");
        ClassName namedBoards = ClassName.get("com.union.xiaobo", "TestForTyype", "Boards");
        TypeName  typeName = ParameterizedTypeName.get(list,className);

        MethodSpec beyond = MethodSpec.methodBuilder("beyond")
                .returns(typeName)
                .addStatement("$T result = new $T<>()", typeName, arrayList)
                .addStatement("result.add($T.createNimbus(2000))", className)
                .addStatement("result.add($T.createNimbus(\"2001\"))", className)
                .addStatement("result.add($T.createNimbus($T.THUNDERBOLT))", className, namedBoards)
                .addStatement("$T.sort(result)", Collections.class)
                .addStatement("return result.isEmpty() ? $T.emptyList() : result", Collections.class)
                .build();

        TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
                .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                .addMethod(beyond)
                .build();

        JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
                .addStaticImport(className,"createNimbus")
                .addStaticImport(className,"namedBoards","*")
                .addStaticImport(Collections.class," * ")
                .build();

        javaFile.writeTo(System.out);

    }

JavaPoet将首先按照配置将导入静态块添加到文件中,然后匹配和错误处理所有调用,并根据需要导入所有其他类型。

$N for Names

生成的代码通常是自引用的。使用$N引用另一个生成的声明的名称:

例如:

public String byteToHex(int b) {
  char[] result = new char[2];
  result[0] = hexDigit((b >>> 4) & 0xf);
  result[1] = hexDigit(b & 0xf);
  return new String(result);
}

public char hexDigit(int i) {
  return (char) (i < 10 ? i + '0' : i - 10 + 'a');
}

在生成上面的代码时,我们使用$N将hexDigit()方法作为参数传递给byteToHex()方法:

  private static void testNames() throws IOException {
       MethodSpec hexDigit =  MethodSpec.methodBuilder("hexDigit")
                .returns(char.class)
                .addParameter(int.class, "i")
                .addStatement("return (char)(i< 10 ? i + '0' :i-10 + 'a')")
                .build();
        MethodSpec byteToHex = MethodSpec.methodBuilder("byteToHex")
                .addParameter(int.class, "b")
                .returns(String.class)
                .addStatement("char[] result = new char[2]")
                .addStatement("result[0] = $N((b >>> 4) & 0xf)", hexDigit)
                .addStatement("result[1] = $N(b & 0xf)", hexDigit)
                .addStatement("return new String(result)")
                .build();

        TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
                .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
                .addMethod(byteToHex)
                .build();

        JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
                .build();

        javaFile.writeTo(System.out);

    }

代码块格式字符串

代码块可以通过几种方式指定占位符的值。代码块上的每个操作只能使用一种样式。

相关参数

将格式字符串中每个占位符的参数值传递给CodeBlock.add()。在每个例子中,我们生成代码说“我吃了3个玉米饼”

CodeBlock.builder().add("I ate $L $L", 3, "tacos")

位置参数

在格式字符串的占位符前放置一个整数索引(基于1),以指定使用哪个参数。

CodeBlock.builder().add("I ate $2L $1L", "tacos", 3)

命名参数

使用$argumentName:X语法,其中X是格式字符,并使用格式字符串中包含所有参数键的映射调用CodeBlock.addNamed()。参数名使用a-z、a-z、0-9和_中的字符,并且必须以小写字符开头。

Map<String, Object> map = new LinkedHashMap<>();
map.put("food", "tacos");
map.put("count", 3);
CodeBlock.builder().addNamed("I ate $count:L $food:L", map)

三、方法

上面所有的方法都有一个代码体。使用修饰符。得到一个没有任何主体的方法。只有当所包含的类是抽象类或接口时,这才是合法的。

MethodSpec flux = MethodSpec.methodBuilder("flux")
    .addModifiers(Modifier.ABSTRACT, Modifier.PROTECTED)
    .build();

TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
    .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
    .addMethod(flux)
    .build();

其他修饰符在允许的地方工作。注意,在指定修饰符时,JavaPoet使用javax.lang.model.element。修饰符,Android上不可用的类。此限制仅适用于代码生成;输出代码可以在任何地方运行:jvm、Android和GWT。

方法还具有参数、异常、varargs、Javadoc、注释、类型变量和返回类型。所有这些都配置了MethodSpec.Builder。

四、构造函数

MethodSpec是一个有点用词不当的词;它也可以用于构造函数:

   private static void testConstructors() throws IOException {
        MethodSpec flux = MethodSpec.constructorBuilder()
                .addModifiers(Modifier.PUBLIC)
                .addParameter(String.class, "greeting")
                .addStatement("this.$N = $N", "greeting", "greeting")
                .build();

        TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
                .addModifiers(Modifier.PUBLIC)
                .addField(String.class, "greeting", Modifier.PRIVATE, Modifier.FINAL)
                .addMethod(flux)
                .build();

        JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
                .build();
        javaFile.writeTo(System.out);
    }

五、参数

使用ParameterSpec.builder()或MethodSpec方便的addParameter() API在方法和构造函数上声明参数:

   private static void testParameters() throws IOException {
        ParameterSpec android = ParameterSpec.builder(String.class,"android",Modifier.PUBLIC).build();
        MethodSpec methodSpec = MethodSpec.methodBuilder("welcomeOverlords")
                .addParameter(android)
                .addParameter(String.class,"roboot",Modifier.FINAL)
                .build();
        TypeSpec typeSpec = TypeSpec.classBuilder("Text").addMethod(methodSpec).build();
        JavaFile javaFile = JavaFile.builder("com.union.xiaobo",typeSpec).build();
        javaFile.writeTo(System.out);

    }

六、字段

与参数一样,字段可以使用构造器创建,也可以使用方便的helper方法创建:

    private static void testField() throws IOException {
        FieldSpec fieldSpec = FieldSpec.builder(String.class,"android",Modifier.FINAL)
                .initializer("$S + $L","Lollipop V", 5.0d)
                .build();

        MethodSpec methodSpec = MethodSpec.methodBuilder("welcomeOverlords")
                .addParameter(String.class,"roboot",Modifier.FINAL)
                .build();
        TypeSpec typeSpec = TypeSpec.classBuilder("Text").addMethod(methodSpec).addField(fieldSpec).build();
        JavaFile javaFile = JavaFile.builder("com.union.xiaobo",typeSpec).build();
        javaFile.writeTo(System.out);

    }	

七、接口

JavaPoet在接口方面没有问题。注意,接口方法必须始终是公共抽象,接口字段必须始终是公共静态FINAL。这些修饰符在定义接口时是必要的:

   private static void testInterface() throws IOException {
        MethodSpec methodSpec = MethodSpec.methodBuilder("welcomeOverlords")
                .addParameter(String.class,"roboot",Modifier.FINAL)
                .build();
        TypeSpec helloWorld = TypeSpec.interfaceBuilder("HelloWorld")
                .addModifiers(Modifier.PUBLIC)
                .addField(FieldSpec.builder(String.class, "ONLY_THING_THAT_IS_CONSTANT")
                        .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
                        .initializer("$S", "change")
                        .build())
                .addMethod(MethodSpec.methodBuilder("beep")
                        .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
                        .build())
                .build();
        JavaFile javaFile = JavaFile.builder("com.union.xiaobo",helloWorld).build();
        javaFile.writeTo(System.out);
    }

八、枚举

使用enumBuilder创建枚举类型,并为每个值添加addEnumConstant():

    private static void testEnum() throws IOException {
        TypeSpec typeSpec = TypeSpec.enumBuilder("Roshambo").addModifiers(Modifier.PUBLIC)
                .addEnumConstant("ROCK")
                .addEnumConstant("SCISSORS")
                .addEnumConstant("PAPER")
                .build();

        JavaFile javaFile = JavaFile.builder("com.union.xiaobo",typeSpec).build();
        javaFile.writeTo(System.out);
    }

支持花式枚举,其中枚举值覆盖方法或调用超类构造函数。下面是一个综合的例子:

  private static void testEnum1() throws IOException {
        TypeSpec helloWorld = TypeSpec.enumBuilder("Roshambo")
                .addModifiers(Modifier.PUBLIC)
                .addEnumConstant("ROCK", TypeSpec.anonymousClassBuilder("$S", "fist")
                        .addMethod(MethodSpec.methodBuilder("toString")
                                .addAnnotation(Override.class)
                                .addModifiers(Modifier.PUBLIC)
                                .addStatement("return $S", "avalanche!")
                                .returns(String.class)
                                .build())
                        .build())
                .addEnumConstant("SCISSORS", TypeSpec.anonymousClassBuilder("$S", "peace")
                        .build())
                .addEnumConstant("PAPER", TypeSpec.anonymousClassBuilder("$S", "flat")
                        .build())
                .addField(String.class, "handsign", Modifier.PRIVATE, Modifier.FINAL)
                .addMethod(MethodSpec.constructorBuilder()
                        .addParameter(String.class, "handsign")
                        .addStatement("this.$N = $N", "handsign", "handsign")
                        .build())
                .build();

        JavaFile javaFile = JavaFile.builder("com.union.xiaobo",helloWorld).build();
        javaFile.writeTo(System.out);
    }

九、匿名内部类

在enum代码中,我们使用了TypeSpec.anonymousInnerClass()。匿名内部类也可以在代码块中使用。这些值可以用$L引用:

    private static void testAnonymous() throws IOException {
        TypeSpec comparator = TypeSpec.anonymousClassBuilder("")
                .addSuperinterface(ParameterizedTypeName.get(Comparator.class, String.class))
                .addMethod(MethodSpec.methodBuilder("compare")
                        .addAnnotation(Override.class)
                        .addModifiers(Modifier.PUBLIC)
                        .addParameter(String.class, "a")
                        .addParameter(String.class, "b")
                        .returns(int.class)
                        .addStatement("return $N.length() - $N.length()", "a", "b")
                        .build())
                .build();

        TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
                .addMethod(MethodSpec.methodBuilder("sortByLength")
                        .addParameter(ParameterizedTypeName.get(List.class, String.class), "strings")
                        .addStatement("$T.sort($N, $L)", Collections.class, "strings", comparator)
                        .build())
                .build();

        JavaFile javaFile = JavaFile.builder("com.union.xiaobo",helloWorld).build();
        javaFile.writeTo(System.out);
    }

十、注解

简单的注解很容易:

   private static void testAnnotations() throws IOException {
        MethodSpec toString = MethodSpec.methodBuilder("toString")
                .addAnnotation(Override.class)
                .returns(String.class)
                .addModifiers(Modifier.PUBLIC)
                .addStatement("return $S", "Hoverboard")
                .build();

        TypeSpec typeSpec = TypeSpec.classBuilder("TestAnnotations").addMethod(toString)
                .build();


        JavaFile javaFile = JavaFile.builder("com.union.xiaobo",typeSpec).build();
        javaFile.writeTo(System.out);
    }

使用AnnotationSpec.builder()在注释上设置属性:

  private static void testAnnotations() throws IOException {
        MethodSpec logRecord = MethodSpec.methodBuilder("recordEvent")
                .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
                .addAnnotation(AnnotationSpec.builder(Headers.class)
                        .addMember("accept", "$S", "application/json; charset=utf-8")
                        .addMember("userAgent", "$S", "Square Cash")
                        .build())
                .addParameter(LogRecord.class, "logRecord")
                .returns(String.class)
                .build();

        TypeSpec typeSpec = TypeSpec.annotationBuilder("TestAnnotations").addMethod(logRecord)
                .build();


        JavaFile javaFile = JavaFile.builder("com.union.xiaobo",typeSpec).build();
        javaFile.writeTo(System.out);
    }

当您开始使用时,注释值可以是注释本身。使用$L嵌入注释:

    private static void testAnnotations1() throws IOException {
        MethodSpec logRecord = MethodSpec.methodBuilder("recordEvent")
                .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
                .addAnnotation(AnnotationSpec.builder(HeaderList.class)
                        .addMember("value", "$L", AnnotationSpec.builder(Header.class)
                                .addMember("name", "$S", "Accept")
                                .addMember("value", "$S", "application/json; charset=utf-8")
                                .build())
                        .addMember("value", "$L", AnnotationSpec.builder(Header.class)
                                .addMember("name", "$S", "User-Agent")
                                .addMember("value", "$S", "Square Cash")
                                .build())
                        .build())
                .addParameter(LogRecord.class, "logRecord")
                .returns(String.class)
                .build();

        TypeSpec typeSpec = TypeSpec.annotationBuilder("TestAnnotations").addMethod(logRecord)
                .build();


        JavaFile javaFile = JavaFile.builder("com.union.xiaobo",typeSpec).build();
        javaFile.writeTo(System.out);
    }

十一、Javadoc

字段、方法和类型可以用Javadoc记录:

    private static void testDoc() throws IOException {
        MethodSpec dismiss = MethodSpec.methodBuilder("dismiss")
                .addJavadoc("Hides {@code message} from the caller's history. Other\n"
                        + "participants in the conversation will continue to see the\n"
                        + "message in their own history unless they also delete it.\n")
                .addJavadoc("\n")
                .addJavadoc("<p>Use {@link #delete($T)} to delete the entire\n"
                        + "conversation for all participants.\n", Conversation.class)
                .addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
                .addParameter(Message.class, "message")
                .build();

        TypeSpec typeSpec = TypeSpec.classBuilder("testDoc").addMethod(dismiss)
                .build();


        JavaFile javaFile = JavaFile.builder("com.union.xiaobo",typeSpec).build();
        javaFile.writeTo(System.out);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值