{"id":17342599,"url":"https://github.com/tobrun/kotlin-data-compat","last_synced_at":"2025-10-11T03:19:03.965Z","repository":{"id":43204339,"uuid":"460909465","full_name":"tobrun/kotlin-data-compat","owner":"tobrun","description":null,"archived":false,"fork":false,"pushed_at":"2024-02-06T14:23:00.000Z","size":157,"stargazers_count":8,"open_issues_count":6,"forks_count":3,"subscribers_count":3,"default_branch":"main","last_synced_at":"2025-04-14T19:52:52.597Z","etag":null,"topics":["java","kotlin","kotlin-library","kotlinpoet","ksp"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tobrun.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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,"zenodo":null}},"created_at":"2022-02-18T15:41:19.000Z","updated_at":"2024-01-19T15:35:14.000Z","dependencies_parsed_at":"2024-01-19T12:39:30.954Z","dependency_job_id":"9fe0c3a9-6b57-43e2-965d-d44f24132102","html_url":"https://github.com/tobrun/kotlin-data-compat","commit_stats":null,"previous_names":[],"tags_count":12,"template":false,"template_full_name":null,"purl":"pkg:github/tobrun/kotlin-data-compat","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tobrun%2Fkotlin-data-compat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tobrun%2Fkotlin-data-compat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tobrun%2Fkotlin-data-compat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tobrun%2Fkotlin-data-compat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tobrun","download_url":"https://codeload.github.com/tobrun/kotlin-data-compat/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tobrun%2Fkotlin-data-compat/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279006098,"owners_count":26084026,"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-10-11T02:00:06.511Z","response_time":55,"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":["java","kotlin","kotlin-library","kotlinpoet","ksp"],"created_at":"2024-10-15T16:06:24.669Z","updated_at":"2025-10-11T03:19:03.949Z","avatar_url":"https://github.com/tobrun.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Kotlin-data-compat\n\n### Context\nThe Data-compat library resolves the Java binary incompatibility that library developers face when using Kotlin data classes. In a nutshell, every change to a data class results in major breaking change. This project attempts to resolve this binary incompatibility by using annotation processing. Users can keep using their original data classes definitions, but the generated code will compatible for Java consumption.\nTo read more about this incompatibility, please refer to Jake Wharton's [Public API challenges in Kotlin](https://jakewharton.com/public-api-challenges-in-kotlin/) blogpost.\n\nThis library uses [Kotlin Symbol Processing API (KSP)](https://kotlinlang.org/docs/ksp-overview.html) in combination with [KotlinPoet](https://square.github.io/kotlinpoet/) to generate Kotlin classes. Input is a private data class that supports a `@DataCompat` annotation and the code generator outputs a Kotlin class that supports a builder pattern compatible for Java usage.\n\n### Add to your project\nThe project is hosted on [jitpack](https://jitpack.io/) and requires to add jitpack.io lookup to your gradle configuration:\n\n```groovy\nallprojects {\n  repositories {\n    maven { url 'https://jitpack.io' }\n  }\n}\n```\n\nSince this project uses ksp, you will have to include it:\n```groovy\nplugins {\n  id 'com.google.devtools.ksp' version '1.6.10-1.0.2' apply false\n}\n```\n\nAnd you will have to include the required dependencies:\n\n```groovy\ndependencies {\n  implementation 'com.github.tobrun.kotlin-data-compat:annotation:0.9.0'\n  ksp 'com.github.tobrun.kotlin-data-compat:processor:0.9.0'\n}\n```\n\n\n### Getting started\n\nGiven an existing data class:\n - add `@DataCompat` annotation\n - mark class private\n - append `Data` to the class name\n - support default parameters by using `@Default` annotation\n - support imports for default parameters\n - retain existing class annotations (but not parameters for them)\n - retain existing interfaces\n - make non-nullable parameters without defaults mandatory Builder constructor parameters\n\nFor example:\n\n```kotlin\nimport com.tobrun.datacompat.annotation.DataCompat\nimport com.tobrun.datacompat.annotation.Default\n\ninterface SampleInterface\nannotation class SampleAnnotation\n\n/**\n * Represents a person.\n * @property name The full name.\n * @property nickname The nickname.\n * @property age The age.\n */\n@DataCompat(importsForDefaults = [\"java.util.Date\"])\n@SampleAnnotation\nprivate data class PersonData(\n    @Default(\"\\\"John\\\" + Date(1580897313933L).toString()\")\n    val name: String,\n    /**\n     * Additional comment.\n     */\n    val nickname: String?,\n    @Default(\"42\")\n    val age: Int,\n    @Default(\"50.2f\")\n    val euroAmount: Float,\n    @Default(\"300.0\")\n    val dollarAmount: Double?,\n    /**\n     * Extra info. Mandatory property.\n     */\n    val extraInfo: String,\n) : SampleInterface\n```\n\nAfter compilation, the following class will be generated:\n\n```kotlin\n@file:Suppress(\"RedundantVisibilityModifier\")\n\npackage com.tobrun.`data`.compat.example\n\nimport java.util.Date\nimport java.util.Objects\nimport kotlin.Any\nimport kotlin.Boolean\nimport kotlin.Double\nimport kotlin.Float\nimport kotlin.Int\nimport kotlin.String\nimport kotlin.Unit\nimport kotlin.jvm.JvmSynthetic\n\n/**\n * Represents a person.\n * @property name The full name.\n * @property nickname The nickname.\n * @property age The age.\n */\n@SampleAnnotation\npublic class Person private constructor(\n    /**\n     * Name.\n     */\n    public val name: String,\n    /**\n     * Additional comment.\n     */\n    public val nickname: String?,\n    /**\n     * Age.\n     */\n    public val age: Int,\n    /**\n     * Euro amount.\n     */\n    public val euroAmount: Float,\n    /**\n     * Dollar amount.\n     */\n    public val dollarAmount: Double?,\n    /**\n     * Extra info. Mandatory property.\n     */\n    public val extraInfo: String\n) : SampleInterface {\n    /**\n     * Overloaded toString function.\n     */\n    public override fun toString() = \"\"\"Person(name=$name, nickname=$nickname, age=$age,\n      euroAmount=$euroAmount, dollarAmount=$dollarAmount, extraInfo=$extraInfo)\"\"\".trimIndent()\n\n    /**\n     * Overloaded equals function.\n     */\n    public override fun equals(other: Any?): Boolean {\n        if (this === other) return true\n        if (javaClass != other?.javaClass) return false\n        other as Person\n        return name == other.name \u0026\u0026 nickname == other.nickname \u0026\u0026 age == other.age \u0026\u0026\n                euroAmount.compareTo(other.euroAmount) == 0 \u0026\u0026\n                (dollarAmount ?: 0.0).compareTo(other.dollarAmount ?: 0.0) == 0 \u0026\u0026\n                extraInfo == other.extraInfo\n    }\n\n    /**\n     * Overloaded hashCode function based on all class properties.\n     */\n    public override fun hashCode(): Int = Objects.hash(name, nickname, age, euroAmount, dollarAmount,\n        extraInfo)\n\n    /**\n     * Convert to Builder allowing to change class properties.\n     */\n    public fun toBuilder(): Builder = Builder(extraInfo) .setName(name) .setNickname(nickname)\n        .setAge(age) .setEuroAmount(euroAmount) .setDollarAmount(dollarAmount)\n        .setExtraInfo(extraInfo)\n\n    /**\n     * Composes and builds a [Person] object.\n     *\n     * This is a concrete implementation of the builder design pattern.\n     */\n    public class Builder(\n        /**\n         * Extra info. Mandatory property.\n         */\n        @set:JvmSynthetic\n        public var extraInfo: String\n    ) {\n        /**\n         * Name.\n         */\n        @set:JvmSynthetic\n        public var name: String = \"John\" + Date(1580897313933L).toString()\n\n        /**\n         * Additional comment.\n         */\n        @set:JvmSynthetic\n        public var nickname: String? = null\n\n        /**\n         * Age.\n         */\n        @set:JvmSynthetic\n        public var age: Int = 42\n\n        /**\n         * Euro amount.\n         */\n        @set:JvmSynthetic\n        public var euroAmount: Float = 50.2f\n\n        /**\n         * Dollar amount.\n         */\n        @set:JvmSynthetic\n        public var dollarAmount: Double? = 300.0\n\n        /**\n         * Setter for name: name.\n         *\n         * @param name\n         * @return Builder\n         */\n        public fun setName(name: String): Builder {\n            this.name = name\n            return this\n        }\n\n        /**\n         * Setter for nickname: additional comment.\n         *\n         * @param nickname\n         * @return Builder\n         */\n        public fun setNickname(nickname: String?): Builder {\n            this.nickname = nickname\n            return this\n        }\n\n        /**\n         * Setter for age: age.\n         *\n         * @param age\n         * @return Builder\n         */\n        public fun setAge(age: Int): Builder {\n            this.age = age\n            return this\n        }\n\n        /**\n         * Setter for euroAmount: euro amount.\n         *\n         * @param euroAmount\n         * @return Builder\n         */\n        public fun setEuroAmount(euroAmount: Float): Builder {\n            this.euroAmount = euroAmount\n            return this\n        }\n\n        /**\n         * Setter for dollarAmount: dollar amount.\n         *\n         * @param dollarAmount\n         * @return Builder\n         */\n        public fun setDollarAmount(dollarAmount: Double?): Builder {\n            this.dollarAmount = dollarAmount\n            return this\n        }\n\n        /**\n         * Setter for extraInfo: extra info. Mandatory property.\n         *\n         * @param extraInfo\n         * @return Builder\n         */\n        public fun setExtraInfo(extraInfo: String): Builder {\n            this.extraInfo = extraInfo\n            return this\n        }\n\n        /**\n         * Returns a [Person] reference to the object being constructed by the builder.\n         *\n         * @return Person\n         */\n        public fun build(): Person = Person(name, nickname, age, euroAmount, dollarAmount, extraInfo)\n    }\n}\n\n/**\n * Creates a [Person] through a DSL-style builder.\n *\n * @param initializer the initialisation block\n * @return Person\n */\n@JvmSynthetic\npublic fun Person(extraInfo: String, initializer: Person.Builder.() -\u003e Unit): Person =\n    Person.Builder(extraInfo).apply(initializer).build()\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftobrun%2Fkotlin-data-compat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftobrun%2Fkotlin-data-compat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftobrun%2Fkotlin-data-compat/lists"}