{"id":19169391,"url":"https://github.com/ravidsrk/kotlin-migration-guide","last_synced_at":"2025-05-07T14:44:24.148Z","repository":{"id":73926653,"uuid":"108891427","full_name":"ravidsrk/kotlin-migration-guide","owner":"ravidsrk","description":"[WIP] Curated List of Practical Tips to migrate your Android App to Kotlin","archived":false,"fork":false,"pushed_at":"2017-12-19T12:38:34.000Z","size":20,"stargazers_count":9,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-04-19T23:32:04.269Z","etag":null,"topics":["kotlin","kotlin-android","kotlin-android-extensions","kotlin-language"],"latest_commit_sha":null,"homepage":"","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/ravidsrk.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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}},"created_at":"2017-10-30T18:26:09.000Z","updated_at":"2025-01-11T06:51:03.000Z","dependencies_parsed_at":null,"dependency_job_id":"c7b4eeeb-7f74-44fd-8239-b452f1133693","html_url":"https://github.com/ravidsrk/kotlin-migration-guide","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ravidsrk%2Fkotlin-migration-guide","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ravidsrk%2Fkotlin-migration-guide/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ravidsrk%2Fkotlin-migration-guide/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ravidsrk%2Fkotlin-migration-guide/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ravidsrk","download_url":"https://codeload.github.com/ravidsrk/kotlin-migration-guide/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252897902,"owners_count":21821526,"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","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":["kotlin","kotlin-android","kotlin-android-extensions","kotlin-language"],"created_at":"2024-11-09T09:46:55.237Z","updated_at":"2025-05-07T14:44:24.110Z","avatar_url":"https://github.com/ravidsrk.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Kotlin Migration Guide\n\n\u003e  Practical Tips to migrate your Android App to Kotlin\n\n---\n\n## Contents\n\n- [Configure Kotlin](#configure-kotlin)\n- [Steps to **Convert**](#steps-to-convert)\n- [Comparing the type system between `Java` and `Kotlin`](#comparing-the-type-system-between-java-and-kotlin)\n  - [`Any` v/s `Object`](#any-vs-object)\n- [Type inference](#type-inference)\n- [What `nullability` means in Kotlin?](#what-nullability-means-in-kotlin)\n- [Why `Optionals` exist?](#why-optionals-exist)\n- [How `data` classes are based on Value Types](#how-data-classes-are-based-on-value-types)\n  - [`class` v/s `data-class`](#class-vs-data-class)\n- [Using `Parcelize` annotation for `Parcelable`](#using-parcelize-annotation-for-parcelable)\n- [String templates](#string-templates)\n- [Removing `ButterKnife` and `findViewById` to use `Kotlin-Android-Extensions`](#removing-butterknife-and-findviewbyid-to-use-kotlin-android-extensions)\n- [`apply`, `let`, `with` and `also`](#apply-let-with-and-also)\n- [Common Migration Gotchas](#common-migration-gotchas)\n  - [`annotationProcessor` must be replaced by `kapt` in build.gradle](#annotationprocessor-must-be-replaced-by-kapt-in-buildgradle)\n  - [Configure tests to mock `final` classes](#configure-tests-to-mock-final-classes)\n  - [If you are using android `data-binding`, include:](#if-you-are-using-android-data-binding-include)\n  - [`@JvmField` to rescue while using ButterKnife `@InjectView` and Espresso `@Rule`](#jvmfield-to-rescue-while-using-butterknife-injectview-and-espresso-rule)\n- [Common Converter Gotchas](##common-converter-gotchas)\n  - [`TypeCasting` for the sake of `Interoperability`](#typecasting-for-the-sake-of-interoperability)\n  - [`Companion` will add extra layer](#companion-will-add-extra-layer)\n  - [Method names starting with `get`](#method-names-starting-with-get)\n- [Eliminating `!!` from your Kotlin code](#eliminating--from-your-kotlin-code)\n  - [Use `val` instead of `var`](#use-val-instead-of-var)\n  - [No `ArgumentCaptor`](#no-argumentcaptor)\n  - [Use `lateinit`](#use-lateinit)\n  - [Use `let` function](#use-let-function)\n  - [Use `elvis` operator](#use-elvis-operator)\n- [Annotations to make Kotlin interoperable with Java.](#annotations-to-make-kotlin-interoperable-with-java)\n- [Migrating Unit Tests to Use `Mockito-Kotlin`](#migrating-unit-tests-to-use-mockito-kotlin)\n- [Utils -\u003e Kotlin-Extensions](#utils---kotlin-extensions)\n- [Migrating Room to Kotlin](#migrating-room-to-kotlin)\n- [Idiomatic Kotlin](#idiomatic-kotlin)\n- [Acknowledgement ](#acknowledgement)\n- [References](#references)\n\n\n## Configure Kotlin\n\n1. In Android Studio, select **Tools \u003e Kotlin \u003e Configure Kotlin in Project**. If a window titled **Choose Configurator** appears, select **Android with Gradle**, make sure **All modules** is selected, and click **OK**.\n2. You will be prompted to sync the project with Gradle as the build.gradle files have changed. Once the sync is complete, move to the next step and write some Kotlin code.\n\n**Note**: When using Android Studio 3.0 or higher and creating a new project, you can have Kotlin configured by default by selecting the **Include Kotlin support** checkbox on the Create Android Project dialog.\n\n---\n\n## Steps to **Convert**\n\nOnce you learn basics syntax of **Kotlin**\n\n1. Convert files, one by one, via **\"⌥⇧⌘K\",** make sure tests still pass\n2. Go over the Kotlin files and make them more [idiomatic](https://blog.philipphauer.de/idiomatic-kotlin-best-practices/).\n3. Repeat step 2 until you convert all the files.\n4. Ship it.\n\n---\n\n## Comparing the type system between `Java` and `Kotlin`\n\n\n\n---\n\n### `Any` v/s `Object`\n\nAll classes in Kotlin have a common superclass Any, that is a default super for a class with no supertypes declared:\n\n```kotlin\nclass Example // Implicitly inherits from Any\n```\n\n\u003e **Any is not java.lang.Object**; in particular, it does not have any members other than `equals()`, `hashCode()` and `toString()`. Please consult the Java interoperability section for more details.\n\nFurther, from the section on mapped types we find:\n\nKotlin treats some Java types specially. Such types are not loaded from Java “as is”, but are mapped to corresponding Kotlin types. The mapping only matters at compile time, the runtime representation remains unchanged. Java’s primitive types are mapped to corresponding Kotlin types (keeping platform types in mind):\n\n\u003e `java.lang.Object` `kotlin.Any!`\n\nThis says that at **runtime** `java.lang.Object` and `kotlin.Any!` are treated the same. But the `!`also means that the type is a platform type, which has implication with respect to disabling null checks etc.\n\n\u003e Any reference in Java may be null, which makes Kotlin’s requirements of strict null-safety impractical for objects coming from Java. Types of Java declarations are treated specially in Kotlin and called platform types. Null-checks are relaxed for such types, so that safety guarantees for them are the same as in Java (see more below).\n\n\u003e When we call methods on variables of platform types, Kotlin does not issue nullability errors at compile time, but the call may fail at runtime, because of a null-pointer exception or an assertion that Kotlin generates to prevent nulls from propagating:\n\n---\n\n## Type inference\n\n```kotlin\n// val abc: Map\u003cString,Int\u003e = mapOf(\"a\" to 1, \"b\" to 2, \"c\" to 3)\n// the actual type of the Map is obvious\n// thus we can skip Map\u003cString,Int\u003e and let kotlin compiler to make type inference\nval abc = mapOf(\"a\" to 1, \"b\" to 2, \"c\" to 3)\n\nval abcd = abc + (\"d\" to 4) // ok!\nval abce = abc + (\"e\" to \"5\") // compile error: Type mismatch\n\n// val c: Int? = abc[\"c\"]\nvar c = abc[\"c\"] \n\nc = \"2\" // compile error: Type mismatch\n\n// Same Question: \n// If you are asked to refactor from Map\u003cString,Integer\u003e to Map\u003cString,String\u003e,\n// try to estimate the efforts!\n```\n\n\n\n---\n\n## What `nullability` means in Kotlin?\n[Source](https://kotlinlang.org/docs/reference/null-safety.html)\n\n\n\n---\n\n## Why `Optionals` exist?\n\n\n\n---\n\n## How `data` classes are based on Value Types\n\n\n\n---\n\n### `class` v/s `data-class`\n\n\n\n---\n\n## Using `Parcelize` annotation for `Parcelable`\n[Source](https://github.com/Kotlin/KEEP/blob/master/proposals/extensions/android-parcelable.md)\n\nHere is the Java Class:\n\n```java\npublic class MyParcelable implements Parcelable {\n     private int mData;\n\n     public int describeContents() {\n         return 0;\n     }\n\n     public void writeToParcel(Parcel out, int flags) {\n         out.writeInt(mData);\n     }\n\n     public static final Parcelable.Creator\u003cMyParcelable\u003e CREATOR\n             = new Parcelable.Creator\u003cMyParcelable\u003e() {\n         public MyParcelable createFromParcel(Parcel in) {\n             return new MyParcelable(in);\n         }\n\n         public MyParcelable[] newArray(int size) {\n             return new MyParcelable[size];\n         }\n     };\n     \n     private MyParcelable(Parcel in) {\n         mData = in.readInt();\n     }\n }\n```\n\nConverted Kotlin Class:\n\n```kotlin\ndata class MyParcelable(var data: Int): Parcelable {\n\n    override fun describeContents() = 1\n\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        dest.writeInt(data)\n    }\n\n    companion object {\n        @JvmField \n        val CREATOR = object : Parcelable.Creator\u003cMyParcelable\u003e {\n            override fun createFromParcel(source: Parcel): MyParcelable {\n                val data = source.readInt()\n                return MyParcelable(data)\n            }\n\n            override fun newArray(size: Int) = arrayOfNulls\u003cMyParcelable\u003e(size)\n        }\n    }\n}\n```\n\nTo use `@Parcelize` we need to set experimental flag in `build.gradle`\n\n```groovy\nandroidExtensions {\n    experimental = true\n}\n```\n\nAndroid Extensions plugin now includes an automatic [Parcelable](https://developer.android.com/reference/android/os/Parcelable.html) implementation generator. Declare the serialized properties in a primary constructor and add a `@Parcelize` annotation, and `writeToParcel()`/`createFromParcel()` methods will be created automatically:\n\n```kotlin\n@Parcelize\nclass MyParcelable(val data: Int): Parcelable\n```\n\n---\n\n## String templates\n\nKotlin has string templates, which is awesome. e.g. \"$firstName $lastName\" for simple variable name or \"${person.name} is ${1 * 2}\" for any expressions. You can still do the string concatenation if you like e.g. \"hello \" + \"world\", but that means being stupid.\n\n---\n\n## Removing `ButterKnife` and `findViewById` to use `Kotlin-Android-Extensions`\n\n\n\n\n\n---\n\n## `apply`, `let`, `with` and `also`\n\n\n\n---\n\n## Common Migration Gotchas\n\n* **annotationProcessor** must be replaced by **kapt** in build.gradle\n* Configure tests to mock final classes\n* `@JvmField` to rescue while using ButterKnife `@InjectView` and Espresso `@Rule`\n\n---\n\n## If you are using android **data-binding**, include:\n\n```groovy\nkapt \"com.android.databinding:compiler:${compiler_version}\"\n```\n\nFixes following the Gradle warning:\n```\nWarning:warning: The following options were not recognized by any processor: '[android.databinding.artifactType,\nandroid.databinding.printEncodedErrors, android.databinding.minApi, android.databinding.isTestVariant,\nandroid.databinding.enableDebugLogs, android.databinding.sdkDir, android.databinding.bindingBuildFolder,\nandroid.databinding.enableForTests, android.databinding.modulePackage, kapt.kotlin.generated, \nandroid.databinding.generationalFileOutDir, android.databinding.xmlOutDir]'\n```\n\n---\n\n## Common Converter Gotchas\n\n* TypeCasting for the sake of Interoperability.\n* **Companion** will add extra layer.\n* If java method starting with getX(), converter looks for property with the name X.\n* **Generics** is hard to get it right on the first go.\n* No argument captor.\n* **git diff** If two developers are working on same java file and one guy converts it to Kotlin, it will be rework.\n\n\n---\n\n### `TypeCasting` for the sake of `Interoperability`\n\nKotlin is not Interoperable right away, but you need to do a lot of work around to make it Interoperable\n\nHere is the **Java** class:\n\n```java\npublic class DemoFragment extends BaseFragment implements DemoView {\n    @Override public void displayMessageFromApi(String apiMessage) {\n      ...\n    }\n}\n```\n\n---\n\n```kotlin\n// Kotlin class\nclass DemoResponse {\n    @SerializedName(\"message\") var message: String? = null\n}\n\n// Typecasting to String\nmainView?.displayMessageFromApi(demoResponse.message as String)\n```\n\n---\n\n### `Companion` will add extra layer\n\nHere is **Java** class:\n\n```java\npublic class DetailActivity extends BaseActivity implements DetailMvpView{\n    public static final String EXTRA_POKEMON_NAME = \"EXTRA_POKEMON_NAME\";\n\n    public static Intent getStartIntent(Context context, String pokemonName) {\n        Intent intent = new Intent(context, DetailActivity.class);\n        intent.putExtra(EXTRA_POKEMON_NAME, pokemonName);\n        return intent;\n    }\n}\n```\n---\n\nConverted  **Kotlin** class:\n\n```kotlin\nclass DetailActivity : BaseActivity(), DetailMvpView {\n    companion object {\n        val EXTRA_POKEMON_NAME = \"EXTRA_POKEMON_NAME\"\n\n        fun getStartIntent(context: Context, pokemonName: String): Intent {\n            val intent = Intent(context, DetailActivity::class.java)\n            intent.putExtra(EXTRA_POKEMON_NAME, pokemonName)\n            return intent\n        }\n    }\n}\n```\n---\n\n```java\npublic class MainActivity extends BaseActivity implements MainMvpView {\n  private void pokemonClicked(Pokemon pokemon) {\n      startActivity(DetailActivity.Companion.getStartIntent(this, pokemon))    \n  }\n}\n```\n\n---\n\n### Method names starting with `get`\n\nHere is the Java class:\n\n```java\npublic interface DemoService {\n    @GET(\"posts\")\n    Observable\u003cPostResponse\u003e getDemoResponse();\n\n    @GET(\"categories\")\n    Observable\u003cCategoryResponse\u003e getDemoResponse2();\n}\n```\n\n---\n\nConverted  **Kotlin** class:\n\n```kotlin\ninterface DemoService {\n    @get:GET(\"posts\")\n    val demoResponse: Observable\u003cPostResponse\u003e\n    \n    @get:GET(\"categories\")\n    val demoResponse2: Observable\u003cCategotyResponse\u003e\n}\n```\n\nExpecting methods **demoResponse** and **demoResponse2**, They are being interpreted as getter methods, this will cause lots of issues.\n\n---\n\n## Eliminating `!!` from your Kotlin code\n[Source](https://android.jlelse.eu/how-to-remove-all-from-your-kotlin-code-87dc2c9767fb)\n\n* Use **val** instead of **var**\n* Use **lateinit**\n* Use **let** function\n* User **Elivis** operator\n\n---\n\n### Use `val` instead of `var`\n\n* Kotlin makes you think about immutability on the language level and that’s great.\n* **var** and **val** mean **\"writable\"** and **\"read-only\"**\n* If you use them as immutables, you don’t have to care about nullability.\n\n---\n\n### No `ArgumentCaptor`\n\nIf you are using Mockito’s ArgumentCaptor, you will most probably get following error\n\n```\njava.lang.IllegalStateException: classCaptor.capture() must not be null\n```\n\nThe return value of **classCaptor.capture()** is null, but the signature of **SomeClass#someMethod(Class, Boolean)** does not allow a *null* argument.\n\n---\n\n\n### Use `lateinit`\n\n```kotlin\nprivate var adapter: RecyclerAdapter\u003cDroids\u003e? = null\n\noverride fun onCreate(savedInstanceState: Bundle?) {\n   super.onCreate(savedInstanceState)\n   adapter = RecyclerAdapter(R.layout.item_droid)\n}\n\nfun updateTransactions() {\n   adapter!!.notifyDataSetChanged()\n}\n\n```\n\n---\n\n```kotlin\nprivate lateinit var adapter: RecyclerAdapter\u003cDroids\u003e\n\noverride fun onCreate(savedInstanceState: Bundle?) {\n   super.onCreate(savedInstanceState)\n   adapter = RecyclerAdapter(R.layout.item_droid)\n}\n\nfun updateTransactions() {\n   adapter?.notifyDataSetChanged()\n}\n\n```\n\n---\n\n### Use `let` function\n\n```kotlin\nprivate var photoUrl: String? = null\n\nfun uploadClicked() {\n    if (photoUrl != null) {\n        uploadPhoto(photoUrl!!)\n    }\n}\n```\n\n---\n\n```kotlin\nprivate var photoUrl: String? = null\n\nfun uploadClicked() {\n    photoUrl?.let { uploadPhoto(it) }\n}\n\n```\n\n---\n\n### Use `elvis` operator\n\nElvis operator is great when you have a fallback value for the null case. So you can replace this:\n\n```kotlin\nfun getUserName(): String {\n   if (username != null) {\n       return username!!\n   } else {\n       return \"Anonymous\"\n   }\n}\n```\n\n----\n\n**elvis** operator is great when you have a fallback value for the null case. So you can replace this:\n\n```kotlin\nfun getUserName(): String {\n   return username ?: \"Anonymous\"\n}\n```\n\n---\n\n## Annotations to make Kotlin interoperable with Java.\n\n\n\n---\n\n## Migrating Unit Tests to Use [`Mockito-Kotlin`](https://github.com/nhaarman/mockito-kotlin)\n\n\n\n---\n\n## Utils -\u003e Kotlin-Extensions\n\n\n\n---\n\n## Migrating Room to Kotlin\n\n\n\n---\n\n## Idiomatic Kotlin\n\n\n\n---\n\n## Acknowledgement \n\n[**Arun Sasidharan**](https://github.com/esoxjem) for Initial Idea\n\n[**Ritesh Gupta**](https://github.com/riteshhgupta) for More Ideas\n\n---\n\n## References\n\nhttps://medium.com/fueled-android/practical-tips-to-migrate-your-android-app-to-kotlin-4d331e5256dc\nhttps://codelabs.developers.google.com/codelabs/taking-advantage-of-kotlin/index.html\nhttps://android.jlelse.eu/how-to-remove-all-from-your-kotlin-code-87dc2c9767fb\nhttps://medium.com/google-developers/migrating-an-android-project-to-kotlin-f93ecaa329b7\nhttps://blog.philipphauer.de/idiomatic-kotlin-best-practices/\nhttps://stackoverflow.com/a/38761552/1257042\nhttps://gist.github.com/gaplo917/c3b70249b660bff9cdc0b5453c34f98b\n\n---\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fravidsrk%2Fkotlin-migration-guide","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fravidsrk%2Fkotlin-migration-guide","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fravidsrk%2Fkotlin-migration-guide/lists"}