diff --git a/README.md b/README.md index 0cfbddc01..1f3490daa 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,30 @@ In this case we write the file to `System.out`, but we could also get it as a st The [Javadoc][javadoc] catalogs the complete JavaPoet API, which we explore below. +### Record + +From Java 14, support new type for creating object is Record (`record`) + +There is how JavaPoet API support record: + +```java +JavaFile javaFile = JavaFile.builder("com.squareup.tacos", TypeSpec + .recordBuilder("Taco") + .addField(FieldSpec.builder(String.class, "name").build()) + .addField(FieldSpec.builder(Integer.class, "code").build()) + .skipJavaLangImports(true) + .build()); +``` + +Which generates: + +```java +package com.squareup.tacos; + +record Taco(String name, Integer code) { +} +``` + ### Code & Control Flow Most of JavaPoet's API uses plain old immutable Java objects. There's also builders, method chaining diff --git a/src/main/java/com/squareup/javapoet/TypeSpec.java b/src/main/java/com/squareup/javapoet/TypeSpec.java index 6cd2ea636..02abf2e80 100644 --- a/src/main/java/com/squareup/javapoet/TypeSpec.java +++ b/src/main/java/com/squareup/javapoet/TypeSpec.java @@ -132,6 +132,15 @@ public static Builder classBuilder(ClassName className) { return classBuilder(checkNotNull(className, "className == null").simpleName()); } + public static Builder recordBuilder(String name) { + return new Builder(Kind.RECORD, checkNotNull(name, "name == null"), null); + } + + public static Builder recordBuilder(ClassName className) { + return recordBuilder(checkNotNull(className, "className == null").simpleName()); + } + + public static Builder interfaceBuilder(String name) { return new Builder(Kind.INTERFACE, checkNotNull(name, "name == null"), null); } @@ -228,6 +237,26 @@ void emit(CodeWriter codeWriter, String enumName, Set implicitModifier if (kind == Kind.INTERFACE) { extendsTypes = superinterfaces; implementsTypes = Collections.emptyList(); + } else if (kind == Kind.RECORD) { + extendsTypes = Collections.emptyList(); + implementsTypes = superinterfaces; + + // Record constructor + boolean firstParameter = true; + codeWriter.emit("("); + int fieldSpecsLength = fieldSpecs.size(); + for (int i = 0; i < fieldSpecsLength; i++) { + FieldSpec fieldSpec = fieldSpecs.get(i); + + if (fieldSpec.hasModifier(Modifier.STATIC)) + continue; + ParameterSpec parameter = ParameterSpec.builder(fieldSpec.type, fieldSpec.name).build(); + if (!firstParameter) + codeWriter.emit(",").emitWrappingSpace(); + parameter.emit(codeWriter, !(i < fieldSpecsLength)); + firstParameter = false; + } + codeWriter.emit(")"); } else { extendsTypes = superclass.equals(ClassName.OBJECT) ? Collections.emptyList() @@ -295,11 +324,16 @@ void emit(CodeWriter codeWriter, String enumName, Set implicitModifier } // Non-static fields. - for (FieldSpec fieldSpec : fieldSpecs) { - if (fieldSpec.hasModifier(Modifier.STATIC)) continue; - if (!firstMember) codeWriter.emit("\n"); - fieldSpec.emit(codeWriter, kind.implicitFieldModifiers); - firstMember = false; + // If kind RECORD, ignore generate Non-static field + if (!(Kind.RECORD == kind)) { + for (FieldSpec fieldSpec : fieldSpecs) { + if (fieldSpec.hasModifier(Modifier.STATIC)) + continue; + if (!firstMember) + codeWriter.emit("\n"); + fieldSpec.emit(codeWriter, kind.implicitFieldModifiers); + firstMember = false; + } } // Initializer block. @@ -374,6 +408,9 @@ public enum Kind { Collections.emptySet(), Collections.emptySet()), + RECORD(Collections.emptySet(), Collections.emptySet(), Collections.emptySet(), + Collections.emptySet()), + INTERFACE( Util.immutableSet(Arrays.asList(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)), Util.immutableSet(Arrays.asList(Modifier.PUBLIC, Modifier.ABSTRACT)), diff --git a/src/test/java/com/squareup/javapoet/JavaFileTest.java b/src/test/java/com/squareup/javapoet/JavaFileTest.java index e75a01961..6aa5d9458 100644 --- a/src/test/java/com/squareup/javapoet/JavaFileTest.java +++ b/src/test/java/com/squareup/javapoet/JavaFileTest.java @@ -285,6 +285,26 @@ private TypeSpec importStaticTypeSpec(String name) { + "}\n"); } + @Test + public void recordNoField() { + String source = JavaFile.builder("com.squareup.tacos", TypeSpec.recordBuilder("Taco").build()) + .skipJavaLangImports(true).build().toString(); + assertThat(source).isEqualTo( + "" + "package com.squareup.tacos;\n" + "\n" + "record Taco() {\n" + "}\n" + ""); + } + + @Test + public void recordTwoField() { + String source = JavaFile + .builder("com.squareup.tacos", + TypeSpec.recordBuilder("Taco") + .addField(FieldSpec.builder(String.class, "name").build()) + .addField(FieldSpec.builder(Integer.class, "code").build()).build()) + .skipJavaLangImports(true).build().toString(); + assertThat(source).isEqualTo("" + "package com.squareup.tacos;\n" + "\n" + + "record Taco(String name, Integer code) {\n" + "}\n" + ""); + } + @Test public void conflictingImports() throws Exception { String source = JavaFile.builder("com.squareup.tacos", TypeSpec.classBuilder("Taco")