1## Kotlin-specific guidelines {#kotlin}
2
3Generally speaking, Kotlin code should follow the compatibility guidelines
4outlined at:
5
6-   The official Android Developers
7    [Kotlin-Java interop guide](https://developer.android.com/kotlin/interop)
8-   Android API guidelines for
9    [Kotlin-Java interop](https://android.googlesource.com/platform/developers/docs/+/refs/heads/master/api-guidelines/index.md#kotin-interop)
10-   Android API guidelines for
11    [asynchronous and non-blocking APIs](https://android.googlesource.com/platform/developers/docs/+/refs/heads/master/api-guidelines/async.md)
12-   Library-specific guidance outlined below
13
14### Target language version {#kotlin-target}
15
16All projects in AndroidX compile using the same version of the Kotlin
17compiler -- typically the latest stable version -- and by default use a matching
18*target language version*. The target language version specifies which Kotlin
19features may be used in source code, which in turn specifies (1) which version
20of `kotlin-stdlib` is used as a dependency and thus (2) which version of the
21Kotlin compiler is required when the library is used as a dependency.
22
23Libraries may specify `kotlinTarget` in their `build.gradle` to override the
24default target language version. Using a higher language version will force
25clients to use a newer, typically less-stable Kotlin compiler but allows use of
26newer language features. Using a lower language version will allow clients to
27use an older Kotlin compiler when building their own projects.
28
29```
30androidx {
31    kotlinTarget = KotlinVersion.KOTLIN_1_7
32}
33```
34
35NOTE The client's Kotlin compiler version is bounded by their *transitive
36dependencies*. If your library uses target language version 1.7 but you depend
37on a library with target language version 1.9, the client will be forced to use
381.9 or higher.
39
40### Nullability
41
42#### Annotations on new Java APIs
43
44All new Java APIs should be annotated either `@Nullable` or `@NonNull` for all
45reference parameters and reference return types.
46
47```java
48    @Nullable
49    public Object someNewApi(@NonNull Thing arg1, @Nullable List<WhatsIt> arg2) {
50        if(/** something **/) {
51            return someObject;
52        } else {
53            return null;
54    }
55```
56
57#### Adding annotations to existing Java APIs
58
59Adding `@Nullable` or `@NonNull` annotations to existing APIs to document their
60existing nullability is allowed. This is a source-breaking change for Kotlin
61consumers, and you should ensure that it's noted in the release notes and try to
62minimize the frequency of these updates in releases.
63
64Changing the nullability of an API is a behavior-breaking change and should be
65avoided.
66
67#### Extending APIs that are missing annotations
68
69[Platform types](https://kotlinlang.org/docs/java-interop.html#null-safety-and-platform-types)
70are exposed by Java types that do not have a `@Nullable` or `@NonNull`
71annotation. In Kotlin they are indicated with the `!` suffix.
72
73When interacting with an Android platform API that exposes APIs with unknown
74nullability follow these rules:
75
761.  If wrapping the type in a new API, define and handle `@Nullable` or
77    `@NonNull` in the library. Treat types with unknown nullability passed into
78    or return from Android as `@Nullable` in the library.
792.  If extending an existing API (e.g. `@Override`), pass through the existing
80    types with unknown nullability and annotate each with
81    `@SuppressLint("UnknownNullness")`
82
83In Kotlin, a type with unknown nullability is exposed as a "platform type"
84(indicated with a `!` suffix) which has unknown nullability in the type checker,
85and may bypass type checking leading to runtime errors. When possible, do not
86directly expose types with unknown nullability in new public APIs.
87
88#### Extending `@RecentlyNonNull` and `@RecentlyNullable` APIs
89
90Platform APIs are annotated in the platform SDK artifacts with fake annotations
91`@RecentlyNonNull` and `@RecentlyNullable` to avoid breaking builds when we
92annotated platform APIs with nullability. These annotations cause warnings
93instead of build failures. The `RecentlyNonNull` and `RecentlyNullable`
94annotations are added by Metalava and do not appear in platform code.
95
96When extending an API that is annotated `@RecentlyNonNull`, you should annotate
97the override with `@NonNull`, and the same for `@RecentlyNullable` and
98`@Nullable`.
99
100For example `SpannableStringBuilder.append` is annotated `RecentlyNonNull` and
101an override should look like:
102
103```java
104    @NonNull
105    @Override
106    public SpannableStringBuilder append(@SuppressLint("UnknownNullness") CharSequence text) {
107        super.append(text);
108        return this;
109    }
110```
111
112### Data classes {#kotlin-data}
113
114Kotlin `data` classes provide a convenient way to define simple container
115objects, where Kotlin will generate `equals()` and `hashCode()` for you.
116However, they are not designed to preserve API/binary compatibility when members
117are added. This is due to other methods which are generated for you -
118[destructuring declarations](https://kotlinlang.org/docs/reference/multi-declarations.html),
119and [copying](https://kotlinlang.org/docs/reference/data-classes.html#copying).
120
121Example data class as tracked by metalava:
122
123<pre>
124  public final class TargetAnimation {
125    ctor public TargetAnimation(float target, androidx.animation.AnimationBuilder animation);
126    <b>method public float component1();</b>
127    <b>method public androidx.animation.AnimationBuilder component2();</b>
128    <b>method public androidx.animation.TargetAnimation copy(float target, androidx.animation.AnimationBuilder animation);</b>
129    method public androidx.animation.AnimationBuilder getAnimation();
130    method public float getTarget();
131  }
132</pre>
133
134Because members are exposed as numbered components for destructuring, you can
135only safely add members at the end of the member list. As `copy` is generated
136with every member name in order as well, you'll also have to manually
137re-implement any old `copy` variants as items are added. If these constraints
138are acceptable, data classes may still be useful to you.
139
140As a result, Kotlin `data` classes are *strongly discouraged* in library APIs.
141Instead, follow best-practices for Java data classes including implementing
142`equals`, `hashCode`, and `toString`.
143
144See Jake Wharton's article on
145[Public API challenges in Kotlin](https://jakewharton.com/public-api-challenges-in-kotlin/)
146for more details.
147
148### Flow return type {#flow-return-type}
149
150Always prefer non-null for `Flow` objects, return a Flow that does not emit
151items as a default. One option is `emptyFlow()` which will complete. Another
152option is `flow { awaitCancellation() }` which will not emit and not complete.
153Choose the option that best suites the use-case.
154
155```kotlin
156fun myFlowFunction(): Flow<Data> {
157    return if (canCreateFlow()) {
158        createFlow()
159    } else {
160        emptyFlow()
161    }
162}
163```
164
165### Exhaustive `when` and `sealed class`/`enum class` {#exhaustive-when}
166
167A key feature of Kotlin's `sealed class` and `enum class` declarations is that
168they permit the use of **exhaustive `when` expressions.** For example:
169
170```kotlin
171enum class CommandResult { Permitted, DeniedByUser }
172
173val message = when (commandResult) {
174    Permitted -> "the operation was permitted"
175    DeniedByUser -> "the user said no"
176}
177
178println(message)
179```
180
181This highlights challenges for library API design and compatibility. Consider
182the following addition to the `CommandResult` possibilities:
183
184```kotlin {.bad}
185enum class CommandResult {
186    Permitted,
187    DeniedByUser,
188    DeniedByAdmin // New in androidx.mylibrary:1.1.0!
189}
190```
191
192This change is both **source and binary breaking.**
193
194It is **source breaking** because the author of the `when` block above will see
195a compiler error about not handling the new result value.
196
197It is **binary breaking** because if the `when` block above was compiled as part
198of a library `com.example.library:1.0.0` that transitively depends on
199`androidx.mylibrary:1.0.0`, and an app declares the dependencies:
200
201```kotlin
202implementation("com.example.library:1.0.0")
203implementation("androidx.mylibrary:1.1.0") // Updated!
204```
205
206`com.example.library:1.0.0` does not handle the new result value, leading to a
207runtime exception.
208
209**Note:** The above example is one where Kotlin's `enum class` is the correct
210tool and the library should **not** add a new constant! Kotlin turns this
211semantic API design problem into a compiler or runtime error. This type of
212library API change could silently cause app logic errors or data corruption
213without the protection provided by exhaustive `when`. See
214[When to use exhaustive types](#when-to-use-exhaustive-types).
215
216`sealed class` exhibits the same characteristic; adding a new subtype of an
217existing sealed class is a breaking change for the following code:
218
219```kotlin
220val message = when (command) {
221    is Command.Migrate -> "migrating to ${command.destination}"
222    is Command.Quack -> "quack!"
223}
224```
225
226#### Non-exhaustive alternatives to `enum class`
227
228Kotlin's `@JvmInline value class` with a `private constructor` can be used to
229create type-safe sets of non-exhaustive constants as of Kotlin 1.5. Compose's
230`BlendMode` uses the following pattern:
231
232```kotlin {.good}
233@JvmInline
234value class BlendMode private constructor(val value: Int) {
235    companion object {
236        /** Drop both the source and destination images, leaving nothing. */
237        val Clear = BlendMode(0)
238        /** Drop the destination image, only paint the source image. */
239        val Src = BlendMode(1)
240        // ...
241    }
242}
243```
244
245**Note:** This recommendation may be temporary. Kotlin may add new annotations
246or other language features to declare non-exhaustive enum classes in the future.
247
248Alternatively, the existing `@IntDef` mechanism used in Java-language androidx
249libraries may also be used, but type checking of constants will only be
250performed by lint, and functions overloaded with parameters of different value
251class types are not supported. Prefer the `@JvmInline value class` solution for
252new code unless it would break local consistency with other API in the same
253module that already uses `@IntDef` or compatibility with Java is required.
254
255#### Non-exhaustive alternatives to `sealed class`
256
257Abstract classes with constructors marked as `internal` or `private` can
258represent the same subclassing restrictions of sealed classes as seen from
259outside of a library module's own codebase:
260
261```kotlin
262abstract class Command private constructor() {
263    class Migrate(val destination: String) : Command()
264    object Quack : Command()
265}
266```
267
268Using an `internal` constructor will permit non-nested subclasses, but will
269**not** restrict subclasses to the same package within the module, as sealed
270classes do.
271
272#### When to use exhaustive types
273
274Use `enum class` or `sealed class` when the values or subtypes are intended to
275be exhaustive by design from the API's initial release. Use non-exhaustive
276alternatives when the set of constants or subtypes might expand in a minor
277version release.
278
279Consider using an **exhaustive** (`enum class` or `sealed class`) type
280declaration if:
281
282*   The developer is expected to **accept** values of the type
283*   The developer is expected to **act** on **any and all** values received
284
285Consider using a **non-exhaustive** type declaration if:
286
287*   The developer is expected to **provide** values of the type to APIs exposed
288    by the same module **only**
289*   The developer is expected to **ignore** unknown values received
290
291The `CommandResult` example above is a good example of a type that **should**
292use the exhaustive `enum class`; `CommandResult`s are **returned** to the
293developer and the developer cannot implement correct app behavior by ignoring
294unrecognized result values. Adding a new result value would semantically break
295existing code regardless of the language facility used to express the type.
296
297```kotlin {.good}
298enum class CommandResult { Permitted, DeniedByUser, DeniedByAdmin }
299```
300
301Compose's `BlendMode` is a good example of a type that **should not** use the
302exhaustive `enum class`; blending modes are used as arguments to Compose
303graphics APIs and are not intended for interpretation by app code. Additionally,
304there is historical precedent from `android.graphics` for new blending modes to
305be added in the future.
306
307### Extension and top-level functions {#kotlin-extension-functions}
308
309If your Kotlin file contains any symbols outside of class-like types
310(extension/top-level functions, properties, etc), the file must be annotated
311with `@JvmName`. This ensures unanticipated use-cases from Java callers don't
312get stuck using `BlahKt` files.
313
314Example:
315
316```kotlin {.bad}
317package androidx.example
318
319fun String.foo() = // ...
320```
321
322```kotlin {.good}
323@file:JvmName("StringUtils")
324
325package androidx.example
326
327fun String.foo() = // ...
328```
329
330NOTE This guideline may be ignored for APIs that will only be referenced from
331Kotlin sources, such as Compose.
332
333### Extension functions on platform classes {#kotlin-extension-platform}
334
335While it may be tempting to backport new platform APIs using extension
336functions, the Kotlin compiler will always resolve collisions between extension
337functions and platform-defined methods by calling the platform-defined method --
338even if the method doesn't exist on earlier SDKs.
339
340```kotlin {.bad}
341fun AccessibilityNodeInfo.getTextSelectionEnd() {
342    // ... delegate to platform on SDK 18+ ...
343}
344```
345
346For the above example, any calls to `getTextSelectionEnd()` will resolve to the
347platform method -- the extension function will never be used -- and crash with
348`MethodNotFoundException` on older SDKs.
349
350Even when an extension function on a platform class does not collide with an
351existing API *yet*, there is a possibility that a conflicting API with a
352matching signature will be added in the future. As such, Jetpack libraries
353should avoid adding extension functions on platform classes.
354
355### Extension functions related to classes in the same module or file
356
357When the core type is in Java, one good use for extension functions is to create
358more Kotlin friendly versions of the API.
359
360```java
361public class MyClass {
362    public void addMyListener(Executor e, Consumer<Data> listener) { ... }
363    public void removeMyListener(Consumer<Data> listener) { ... }
364}
365```
366
367```kotlin
368fun MyClass.dataFlow(): Flow<Data>
369```
370
371When the core type is in Kotlin, extension functions may or may not be a good
372fit for the API. Ask if the extension is part of the core abstraction or a new
373layer of abstraction. One example of a new layer of abstraction is using a new
374dependency that does not otherwise interact with the core class. Another example
375is an abstraction that stands on its own such as a `Comparator` that implements
376a non-canonical ordering.
377
378In general when adding extension functions, consider splitting them across
379different files and naming the Java version of the files related to the use case
380as opposed to putting everything in one file and using a `Util` suffix.
381
382```kotlin {.bad}
383@file:JvmName("WindowSizeClassUtil")
384
385fun Set<WindowSizeClass>.widestClass() : WindowSizeClass { ... }
386
387fun WindowSizeClass.scoreWithinWidthDp(widthDp: Int) { ... }
388```
389
390```kotlin
391@file:JvmName("WindowSizeClassSelector")
392
393fun Set<WindowSizeClass>.widestClass() : WindowSizeClass { ... }
394
395// In another file
396@file:JvmName("WindowSizeClassScoreCalculator")
397
398fun WindowSizeClass.scoreWithinWidthDp(widthDp: Int) { ... }
399```
400
401### Function parameters order {#kotlin-params-order}
402
403In Kotlin function parameters can have default values, which are used when you
404skip the corresponding argument.
405
406If a default parameter precedes a parameter with no default value, the default
407value can only be used by calling the function with named arguments:
408
409```kotlin
410fun foo(
411    someBoolean: Boolean = true,
412    someInt: Int,
413) { /*...*/ }
414
415// usage:
416foo(1) // does not compile as we try to set 1 as a value for "someBoolean" and
417       // didn't specify "someInt".
418foo(someInt = 1) // this compiles as we used named arguments syntax.
419```
420
421To not force our users to use named arguments we enforce the following
422parameters order for the public Kotlin functions:
423
4241.  All parameters without default values.
4252.  All parameters with default values.
4263.  An optional last parameter without default value which can be used as a
427    trailing lambda.
428
429### Default interface methods {#kotlin-jvm-default}
430
431The Kotlin compiler is capable of generating Kotlin-specific default interface
432methods that are compatible with Java 7 language level; however, Jetpack
433libraries ship as Java 8 language level and should use the native Java
434implementation of default methods.
435
436To maximize compatibility, Jetpack libraries should pass `-Xjvm-default=all` to
437the Kotlin compiler:
438
439```
440tasks.withType(KotlinCompile).configureEach {
441    kotlinOptions {
442        freeCompilerArgs += ["-Xjvm-default=all"]
443    }
444}
445```
446
447Before adding this argument, library owners must ensure that existing interfaces
448with default methods in stable API surfaces are annotated with
449`@JvmDefaultWithCompatibility` to preserve binary compatibility:
450
4511.  Any interface with stable default method implementations from before the
452    `all` conversion
4531.  Any interface with stable methods that have default argument values from
454    before the `all` conversion
4551.  Any interface that extends another `@JvmDefaultWithCompatibility` interface
456
457Unstable API surfaces do not need to be annotated, e.g. if the methods or whole
458interface is `@RequiresOptIn` or was never released in a stable library version.
459
460One way to handle this task is to search the API `.txt` file from the latest
461release for `default` or `optional` and add the annotation by hand, then look
462for public sub-interfaces and add the annotation there as well.
463