{"id":19777426,"url":"https://github.com/agrosner/kpoet","last_synced_at":"2025-09-01T20:41:03.906Z","repository":{"id":69478109,"uuid":"86388470","full_name":"agrosner/KPoet","owner":"agrosner","description":"An expressive DSL built on top of JavaPoet to make writing code almost as easy as writing the code yourself.","archived":false,"fork":false,"pushed_at":"2020-01-26T12:21:55.000Z","size":158,"stargazers_count":62,"open_issues_count":3,"forks_count":5,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-30T19:42:10.617Z","etag":null,"topics":["annotation-processing","annotation-processor","annotations","codegen","codegeneration","codegenerator","javapoet"],"latest_commit_sha":null,"homepage":null,"language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/agrosner.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-03-27T22:05:01.000Z","updated_at":"2024-12-22T11:07:46.000Z","dependencies_parsed_at":null,"dependency_job_id":"b5fd73a1-a589-4e96-aa8d-f58cfb94d567","html_url":"https://github.com/agrosner/KPoet","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/agrosner/KPoet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agrosner%2FKPoet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agrosner%2FKPoet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agrosner%2FKPoet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agrosner%2FKPoet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/agrosner","download_url":"https://codeload.github.com/agrosner/KPoet/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/agrosner%2FKPoet/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273187916,"owners_count":25060749,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-01T02:00:09.058Z","response_time":120,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["annotation-processing","annotation-processor","annotations","codegen","codegeneration","codegenerator","javapoet"],"created_at":"2024-11-12T05:24:52.585Z","updated_at":"2025-09-01T20:41:03.896Z","avatar_url":"https://github.com/agrosner.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# KPoet\n\nKPoet is a Kotlin extensions library on top of [JavaPoet](https://github.com/square/javapoet) that helps you write code generators / annotation processors that _feel_ like actually writing Java code directly. It provides a Kotlin DSL syntax that resembles real java code as much as possible.\nAlso it attempts to make the code generator writing clear as writing native java code itself.\n\nHere's a (boring) `HelloWorld` class:\n\n```java\npackage com.example.helloworld;\n\npublic final class HelloWorld {\n  public static void main(String[] args) {\n    System.out.println(\"Hello, JavaPoet!\");\n  }\n}\n\n```\n\nIs represented in JavaPoet like:\n\n```java\n\nMethodSpec main = MethodSpec.methodBuilder(\"main\")\n    .addModifiers(Modifier.PUBLIC, Modifier.STATIC)\n    .returns(void.class)\n    .addParameter(String[].class, \"args\")\n    .addStatement(\"$T.out.println($S)\", System.class, \"Hello, JavaPoet!\")\n    .build();\n\nTypeSpec helloWorld = TypeSpec.classBuilder(\"HelloWorld\")\n    .addModifiers(Modifier.PUBLIC, Modifier.FINAL)\n    .addMethod(main)\n    .build();\n\nJavaFile javaFile = JavaFile.builder(\"com.example.helloworld\", helloWorld)\n    .build();\n\njavaFile.writeTo(System.out);\n\n```\n\nWhile JavaPoet provides a very nice library that makes it easier to write code that writes Java code, there are a few problems with vanilla JavaPoet code:\n1. The code is declared in reverse order. You define constructs that are nested inside larger ones (fields, methods, etc) first, then work your way up to the `JavaFile`.\n2. You have to think about how the code will look a lot (especially when it gets more complex), decreasing maintainability.\n3. No order enforced where you declare the spec properties, potentially leading to mistakes and can reduce readibility.\n\n`KPoet` attempts to solve these issues by:\n1. Providing Kotlin DSL extension builders that map closely to native Java code.\n2. Have the code you write for the generator resemble the Java output code closely and in the order you expect, providing both readibility and maintainability.\n3. Also, KPoet provides more concise methods and constructs that will reduce lines of code.\n\nSo using `KPoet` from the previous example:\n\n```kotlin\n\njavaFile(\"com.example.helloworld\") {\n  `class`(\"HelloWorld\") {  modifiers(public, final)\n\n    `public static`(TypeName.VOID, \"main\",\n        param(Array\u003cString\u003e::class, \"args\")) {\n      statement(\"\\$T.out.println(${\"Hello, JavaPoet!\"}.S)\", System::class.java)\n    }\n  }\n}.writeTo(System.out)\n\n```\n\nAs you can see, KPoet takes JavaPoet code and turns it into an expressive DSL that tries to map to regular java as much as possible.\n\nif we want to output a method such as this:\n\n```java\n\npublic boolean handleAction(String action) {\n  switch(action) {\n    case \"bonus\": {\n      this.name = \"BONUS\";\n      break;\n    }\n    default: {\n      this.name = \"NO BONUS\";\n      break;\n    }\n  }\n\n  if (this.name == \"BONUS\") {\n    return true\n  } else if (this.name  == \"NO BONUS\") {\n    return false\n  }\n\n  throw new IllegalStateException(\"Did not process proper action\")\n}\n\n```\n\nWe represent it as:\n\n```kotlin\n\n`public`(TypeName.BOOLEAN, \"handleAction\",\n       param(String::class, \"action\")) {\n  switch(\"action\") {\n    case(\"bonus\".S) {\n      statement(\"this.name = ${\"BONUS\".S}\") // .S wraps it in quotes\n      `break`()\n    }\n    default {\n      statement(\"this.name = ${\"NO BONUS\".S}\")\n      `break`()\n    }\n  }\n\n  `if`(\"this.name == ${\"BONUS\".S}\") {\n    `return`(true.L) // string literal representation with .L\n  }.`else if`(\"this.name == ${\"NO_BONUS\".S}\") {\n    `return`(false.L)\n  }.end() // end required for `if` and `else if`.\n\n  `throw new`(IllegalStateException::class, \"Did not process proper action\")\n}\n\n\n```\n\n## Download\n\nIncluding in your project:\n\n```gradle\n\nallProjects {\n  repositories {\n    // required to find the project's artifacts\n    maven { url \"https://www.jitpack.io\" }\n  }\n}\n```\n\n```gradle\ncompile 'com.squareup:javapoet:1.8.0' // version of JavaPoet currently\ncompile 'com.github.agrosner:KPoet:1.0.0' // version of KPoet\n```\n\nThe next few sections we attempt to mirror the JavaPoet readme, but converted syntax for KPoet, to give you an idea of what the library provides.\n\n\n### Code \u0026 Control Flow\n\nJavaPoet offers APIs to make code generation easier.\n\nWe want to write:\n\n```java\nvoid main() {\n  int total = 0;\n  for (int i = 0; i \u003c 10; i++) {\n    total += i;\n  }\n}\n\n```\n\nAnd so JavaPoet has us write this `MethodSpec`:\n\n```java\n\nMethodSpec main = MethodSpec.methodBuilder(\"main\")\n    .addStatement(\"int total = 0\")\n    .beginControlFlow(\"for (int i = 0; i \u003c 10; i++)\")\n    .addStatement(\"total += i\")\n    .endControlFlow()\n    .build();\n\n```\n\n This is a simple example, but you have to think about what the code will look like when it's generated. Also if you forget to provide a corresponding `endControlFlow()` for every `beginControlFlow()`, it will lead you to runtime crashes that can make it very difficult to diagnose.\n\n With KPoet, you do less thinking about how the code will look:\n\n```kotlin\n\nval main = `fun`(TypeName.VOID, \"main\") {\n  statement(\"int total = 0\")\n  `for`(\"int i = 0; i \u003c 10; i++\") {\n    statement(\"total += i\")\n  }\n}\n\n```\n\ndo..while:\n\n```kotlin\n`do` {\n  statement(\"i++\")\n}.`while`(\"sum \u003c 20\")\n```\n\n### Literals\n\nKPoet has a couple helper methods for cases where we need to pass a literal value to a statement, or code block. The best example is `return`.\n\n```java\naddStatement(\"return \\$L\", someLiteral)\n```\n\ncan easily be replaced with:\n\n```kotlin\n`return`(someLiteral.L)\n```\n\nThis simply converts the object to string, but preserving the JavaPoet-like syntax.\n\n### Strings\n\nWhen using code that includes string literals, JavaPoet uses `$S` to emit a `string`, wrapping quotation marks to escape it.\n\nWith the power of Kotlin string interpolation, we barely need to use $S. For cases where we need to convert it to a string for code output, KPoet provides the `Any?.S` property to simply wrap the object's `toString()` value in quotes.\n\n```kotlin\n\n`public`(String::class, \"getStatus\", param(TypeName.BOOLEAN, \"isReady\")) {\n  `if`(\"isReady\") {\n    `return`(\"BONUS\".S) // if we don't use .S, it's outputted as a literal.\n  } else {\n    `return`(\"NO BONUS\".S)\n  }\n}\n\n```\n\nwhich outputs:\n\n\n```java\npublic String getStatus(boolean isReady) {\n  if (isReady) {\n    return \"BONUS\";\n  } else {\n    return \"NO BONUS\";\n  }\n}\n\n```\n\n### Types\n\nJavaPoet has spectacular handling of reference types by collecting and importing them to make the code much more readable, KPoet does not provide any extension on top of this functionality.\n\nYou will still need to pass that `Class` or `TypeName` to JavaPoet:\n\n```kotlin\n\n`abstract class`(\"TestClass\") {  modifiers(public)\n    field(TypeName.BOOLEAN, isReady, { `=`(false.L) })\n    field(String::class, isReady, { `=`(\"SomeName\".S) })\n\n    `constructor`(param(TypeName.BOOLEAN, isReady)) {\n        statement(\"this.$isReady = $isReady\")\n    }\n}\n\n```\n\n__Be careful__: this library does not convert `KClass\u003c*\u003e` to `Class\u003c*\u003e` in string interpolation with \"\\$T\". However, most of places where `Class` is used in `JavaPoet` we provide the `KClass` version of that.\n\n#### Import Static\n\n`KPoet` supports `import static` pretty easily. When constructing a `JavaFile`,\npass them as the second parameter in the `javaFile` method:\n```kotlin\n\nval file = javaFile(\"com.grosner\", {\n    `import static`(Collections::class, \"*\")\n    `import static`(ClassName.get(String::class.java), \"*\")\n}) {\n    `class`(\"HelloWorld\") {\n        this\n    }\n}\n\n\n```\n\n#### Methods\n\nKPoet supports all kinds of methods.\n\nYou can write `abstract` methods easily:\n\n```kotlin\n\n`abstract class`(\"HelloWorld\") { modifiers(public)\n  abstract(TypeName.VOID, \"flux\") {\n    modifiers(protected)\n  }\n}\n\n```\n\nWhich generates:\n\n```kotlin\npublic abstract class HelloWorld {\n  protected abstract void flux();\n}\n```\n\n#### Constructors\n\nConstructors are fairly easy to write.\n\n```kotlin\n\n`public class`(\"HelloWorld\") {\n  `private final field`(String::class, \"greeting\")\n\n  `constructor`(param(String::class, \"greeting\")) {\n    modifiers(Modifier.PUBLIC)\n    statement(\"this.greeting = greeting\")\n  }\n}\n\n```\n\n#### Parameters\n\nParameters are done via global methods:\n\n```kotlin\n\n`fun`(TypeName.VOID, \"welcomeOverlords\",\n  `final param`(String::class, \"android\"),)\n  `final param`(String::class, \"robot\")\n\n```\n\nWhich generates:\n\n```java\nvoid welcomeOverlords(final String android, final String robot) {\n}\n```\n\nTo add annotations to parameters, simply call:\n\n```kotlin\n\n`fun`(TypeName.VOID, \"welcomeOverlords\",\n  `final param`(`@`(TestAnnotation::class), String::class, \"android\"),\n  `final param`(`@`(TestAnnotation::class, {\n                    this[\"name\"] = \"Some Kind of Member\".S // we use a map to construct the properties here.\n                    this[\"purpose\"] = \"Some Purpose we have\".S\n                }, String::class, \"robot\")))\n```\n\n#### Fields\n\nWe easily add fields to our `TypeSpec` definition:\n\n\n```kotlin\n\n`public class`(\"HelloWorld\") {\n  `private final field`(String::class, \"robot\", { `@`(Nullable::class) }) // can add annotations on fields\n  field(`@`(Nullable::class), String::class, \"android\") { `=`(\"THE BEST\".S)} // or this way\n}\n\n```\n\n#### Enums\n\nuse `enum()` to construct within a `javaFile`:\n\n```kotlin\n\n`enum`(\"Roshambo\") { modifiers(public)\n    case(\"ROCK\",\"fist\".S){\n      `public`(String::class, \"toString\") {\n        `@`(Override::class)\n        `return`(\"avalanche!\".S)\n      }\n    }\n    case(\"SCISSORS\", \"peace\".S)\n    case(\"PAPER\", \"flat\".S)\n\n    `private final field`(String::class, \"handsign\")\n\n    `constructor`(param(String::class, \"handsign\")) {\n      statement(\"this.handsign = handsign\")\n    }\n}\n\n```\n\nwhich generates this:\n\n```java\npublic enum Roshambo {\n  ROCK(\"fist\") {\n    @Override\n    public void toString() {\n      return \"avalanche!\";\n    }\n  },\n\n  SCISSORS(\"peace\"),\n\n  PAPER(\"flat\");\n\n  private final String handsign;\n\n  Roshambo(String handsign) {\n    this.handsign = handsign;\n  }\n}\n```\n\n#### Anonymous Inner Classes\n\nWe write a method that contains a class that contains a method:\n\n```kotlin\n`fun`(TypeName.VOID, \"sortByLength\", param(parameterized\u003cString\u003e(List::class), \"strings\")) {\n  statement(\"\\$T.sort(strings, \\$L)\", Collections::class.java, `anonymous class`(\"\") {\n    extends(parameterized\u003cString\u003e(Comparator::class))\n    `public`(TypeName.INT, \"compare\", param(String::class, \"a\"), param(String::class, \"b\")) {\n      `@`(Override::class)\n      `return`(\"a.length() - b.length()\")\n    }\n  })\n}\n\n```\n\nWhich generates:\n\n```java\n\nvoid sortByLength(List\u003cString\u003e strings) {\n  Collections.sort(strings, new Comparator\u003cString\u003e() {\n    @Override\n    public int compare(String a, String b) {\n      return a.length() - b.length();\n    }\n  });\n}\n\n```\n\n#### Annotations\n\nSimple annotations are easy, just use the \"\\`@()\\`\" method within classes, functions, fields or parameters:\n\nOn methods:\n```Kotlin\n\n`public`(String::class, \"toString\") {\n  `@`(Override::class)\n  `return`(\"Hoverboard\".S)\n}\n\n```\n\non Classes:\n\n```kotlin\n\n`public class`(\"User\") {\n  `@`(Override::class) // annotations have to be within the class block, otherwise we can't associate it with a `class`\n\n}\n\n```\n\nOn fields:\n\n```kotlin\n\nfield(TypeName.BOOLEAN, isReady) {\n  `@`(Override::class)\n  `=`(false.L)\n}\n\n```\n\non parameters:\n\n\n```kotlin\n\n`private`(TypeName.VOID, \"someMethod\",\n  `final param`(`@`(NonNull::class), String::class, \"someParameter\"))\n\n```\n\nOn more complicated cases, say for a class:\n```kotlin\n`public class`(\"User\") {\n  extends(Object::class)\n  `@`(Headers::class, {\n      this[\"accept\"] = \"application/json; charset=utf-8\".S\n      this[\"userAgent\"] = \"Square Cash\".S\n    })\n\n}\n```\n\nIt generates:\n\n```java\n\n@Headers(\n    accept = \"application/json; charset=utf-8\",\n    userAgent = \"Square Cash\"\n)\npublic class User extends Object {\n};\n\n```\n\nFor nested annotations:\n\n```kotlin\n\n`public`(LogReceipt::class, \"recordEvent\", param(LogRecord::class, \"logRecord\")) {\n  modifiers(abstract)\n  `@`(HeaderList::class) {\n    member(\"value\", `@`(Header::class, mapFunc = {\n      this[\"name\"] = \"Accept\".S\n      this[\"value\"] = \"application/json; charset=utf-8\".S\n    }).L)\n    member(\"value\", `@`(Header::class, mapFunc = {\n      this[\"name\"] = \"User-Agent\".S\n      this[\"value\"] = \"Square Cash\".S\n    }).L)\n  }\n}\n\n```\n\n#### JavaDoc\n\nTo add JavaDoc to fields, methods, and types:\n\n```kotlin\n\n`public class`(\"SomeClass\") {\n  javadoc(\"Javadoc goes here\")\n\n  `private final field`(String::class, \"someField\") {\n    javadoc(\"This could be anything you want it to be\")\n    `=`(\"SomeValue\".S)\n  }\n}\n\n`public`(TypeName.VOID, \"dismiss\", param(Message::class, \"message\")) {\n  javadoc(\"Hides {@code message} from the caller's history. Other\\n\"\n        + \"participants in the conversation will continue to see the\\n\"\n        + \"message in their own history unless they also delete it.\\n\")\n  javadoc(\"\\n\")\n  javadoc(\"\u003cp\u003eUse {@link #delete($T)} to delete the entire\\n\"\n        + \"conversation for all participants.\\n\", Conversation.class)\n}\n\n\n\n```\n\n## Pull Requests\nI welcome and encourage all pull requests. It usually will take me within 24-48 hours to respond to any issue or request. Here are some basic rules to follow to ensure timely addition of your request:\n  1. Match coding style (braces, spacing, etc.) This is best achieved using CMD+Option+L (Reformat code) on Mac (not sure for Windows) with Android Studio defaults.\n  2. If its a feature, bugfix, or anything please only change code to what you specify.\n  3. Please keep PR titles easy to read and descriptive of changes, this will make them easier to merge :)\n  4. Pull requests _must_ be made against `develop` branch. Any other branch (unless specified by the maintainers) will get rejected.\n  5. Have fun!\n\n## Maintained By\n[agrosner](https://github.com/agrosner) ([@agrosner](https://www.twitter.com/agrosner))\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fagrosner%2Fkpoet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fagrosner%2Fkpoet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fagrosner%2Fkpoet/lists"}