- https://developer.android.com/kotlin/style-guide
- https://kotlinlang.org/docs/reference/coding-conventions.html
val xs: List<Char> = listOf('a', 'b', 'c')
for ((i, x) in xs.withIndex()) println("$i $x")
// Alternatively
xs.forEachIndexed { i, x -> println("$i $x") }
// Alternatively
xs.mapIndexed { i, x -> "$i $x" }.forEach(::println)
val xs: List<Char> = listOf('a', 'b', 'c').filterIndexed { i, _ -> i != 1 } // [a, c]
// > The returned list has length of the shortest collection.
// https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/zip.html
fun <T> showZipExample(xs: List<T>, ys: List<T>) {
xs.zip(ys).forEach { (x, y) ->
println("$x$y")
}
}
fun main() {
showZipExample(listOf("A", "B"), listOf("a", "b")) // Aa Bb
showZipExample(listOf("A", "B", "C"), listOf("a", "b")) // Aa Bb
showZipExample(listOf("A", "B"), listOf("a", "b", "c")) // Aa Bb
}
Kotlin | Java |
---|---|
IntArray | int[] |
Array<Int> | Integer[] |
val xs: IntArray = intArrayOf(1, 2, 3) // [1, 2, 3]
val ys: IntArray = IntArray(3) // [0, 0, 0]
val zs: IntArray = IntArray(3) { it * 2 } // [0, 2, 4]
val xs: Array<Int> = arrayOf(1, 2, 3) // [1, 2, 3]
// The lambda is not optional.
val ys: Array<Int> = Array(3) { it } // [0, 1, 2]
val zs: Array<String> = Array(3) { "item$it" } // ["item0", "item1", "item2"]
val xs: IntArray = intArrayOf(1, 2, 3)
val ys: Array<Int> = xs.toTypedArray()
val xs: Array<Int> = arrayOf(1, 2, 3)
val ys: IntArray = xs.toIntArray()
val xs: IntArray = intArrayOf(1, 2, 3)
for (i in xs.lastIndex downTo 0) println(xs[i]) // 3 2 1
val xs: Array<String> = arrayOf("a", "b", "c")
for (i in xs.lastIndex downTo 0) println(xs[i]) // c b a
val xs: IntArray = intArrayOf(1, 2, 3)
val ys: IntArray = xs.clone()
val xs: Array<String> = arrayOf("a", "b", "c")
val ys: Array<String> = xs.clone()
val a: List<Int> = emptyList() // equivalent of listOf(). [].
val b: List<Int> = listOf(1, 2, 3) // [1, 2, 3]
val c: List<Int> = listOfNotNull(1, null, 2) // [1, 2]
val d: List<Int> = List(3) { it } // The lambda is not optional. [0, 1, 2]
val e: List<String> = List(3) { "item$it" } // ["item0", "item1", "item2"]
val xs: List<Int> = listOf(1, 2, 3)
val size: Int = xs.size // 3. simpler than xs.count().
val xs = listOf("a", "b", "c")
for (i in xs.lastIndex downTo 0) println(i) // 2 1 0
val xs: List<Int> = listOf(1, 2, 3)
val result1: List<Int> = xs.plus(4) // [1, 2, 3, 4]
val result2: List<Int> = xs.minus(2) // [1, 3]
listOf('a', 'b', 'c')
.onEach { println(it) } // a b c
.map(Char::uppercaseChar)
.forEach { println(it) } // A B C
val xs: List<Int> = listOf(1, 2, 3)
val result1: List<Int> = xs.filter { it == 2 } // [2]
val result2: List<Int> = xs.filterNot { it == 2 } // [1, 3]
val ys: List<Int?> = listOf(1, null, 2)
val result3: List<Int> = ys.filterNotNull() // [1, 2]
val result4: List<Int> = ys.mapNotNull { it.takeIf { it == 2 } } // [2]
val x: List<Int> = emptyList<Int>().ifEmpty { listOf(42) } // 42
val y: List<Int>? = listOf(1, 2, 3).takeIf { 0 in it } // null
val z: List<Int>? = listOf(1, 2, 3).takeUnless { 1 in it } // null
val xs: List<Int> = listOf(1, 2, 3)
val ys: List<Int> = listOf(2, 3, 4)
val result1: List<Int> = xs + ys // [1, 2, 3, 2, 3, 4]
val result2: Set<Int> = xs union ys // [1, 2, 3, 4]
val result3: Set<Int> = xs intersect ys // [2, 3]
val result4: Set<Int> = xs subtract ys // [1]
data class Sample(val name: String)
val xs: List<Int> = listOf(1, 3, 2).sorted() // [1, 2, 3]
val ys: List<Int> = listOf(1, 3, 2).sortedDescending() // [3, 2, 1]
val zs: List<Sample> = listOf(
Sample("c"),
Sample("b"),
Sample("a")
).sortedBy { it.name } // [Sample(name=a), Sample(name=b), Sample(name=c)]
val occurrences: Map<String, Int> =
listOf("b", "c", "b", "c", "a", "c").groupingBy { it }.eachCount() // {a=1, b=2, c=3}
val xs: List<Int>? = null
val ys: List<Int> = x.orEmpty()
// data class Fruit(val name: String, val price: Int)
val fruits: List<Fruit> = listOf(Fruit(name = "apple", price = 100), Fruit(name = "orange", price = 200))
val result: Map<String, Int> = fruits.associate { it.name to it.price } // {apple=100, orange=200}
val xs: List<Char> = listOf('a', 'b', 'c')
val result: Map<Char, Int> = xs.withIndex().associate { it.value to it.index } // {a=0, b=1, c=2}
val xs: List<Char> = listOf('a', 'b', 'c')
val result: Map<Char, Char> = xs.associateWith { it.uppercaseChar() } // {a=A, b=B, c=C}
val xs: List<Char> = listOf('a', 'b', 'c')
val result: Map<Char, Char> = xs.associateBy { it.uppercaseChar() } // {A=a, B=b, C=c}
val xs: List<String> = listOf("fruit" to "apple", "fruit" to "orange", "vegetable" to "carrot", "vegetable" to "potato")
val result: Map<String, List<String>> =
xs.groupBy({ it.first }, { it.second }) // {fruit=[apple, orange], vegetable=[carrot, potato]}
val xs: List<String> = listOf("key1", "key2", "key1", "key2")
val result: Map<String, List<Int>> =
xs.mapIndexed { i, x -> x to i }.groupBy({ it.first }, { it.second }) // {key1=[0, 2], key2=[1, 3]}
val xs: List<Int> = listOf(1, 2, 3)
val ys: List<String> = listOf("a", "b", "c")
val zs: List<String> = xs.zip(ys).map { (x, y) ->
x.toString() + y
} // [1a, 2b, 3c]
val xs: List<Int> = listOf(1, 2, 3)
val ys: List<String> = listOf("a", "b", "c")
val zs: Map<Int, String> = xs.zip(ys).toMap() // {1=a, 2=b, 3=c}
val pair: Pair<String, String> = listOf("a", "b", "c").zipWithNext().first() // (a, b)
val xs: MutableList<String> = mutableListOf("a", "b")
val ys: MutableList<String> = xs.toMutableList()
ys[1] = "c"
println(xs) // [a, b]
println(ys) // [a, c]
val (a, b) = listOf("a", "b", "c")
val set1: Set<String> = setOf("a", "b", "b") // [a, b]
val set2: Set<String> = setOfNotNull(null, "a", "b", "b", null) // [a, b]
enum class Fruit {
APPLE, ORANGE
}
fun main() {
val xs: SortedSet<String> = listOf("c", "b", "a").toSortedSet()
println(xs) // [a, b, c]
val ys: SortedSet<Fruit> = listOf(Fruit.ORANGE, Fruit.APPLE).toSortedSet(compareBy { it.ordinal })
println(ys) // [APPLE, ORANGE]
}
println(sortedSetOf("10", "1", "2")) // [1, 10, 2]
println(sortedSetOf(comparator = reverseOrder(), "10", "1", "2")) // [2, 10, 1]
println(sortedSetOf(comparator = compareBy { it.toInt() }, "10", "1", "2")) // [1, 2, 10]
val set = setOf("10", "1", "2")
println(set.toSortedSet()) // [1, 10, 2]
println(set.toSortedSet(comparator = reverseOrder())) // [2, 10, 1]
println(set.toSortedSet(comparator = compareBy { it.toInt() })) // [1, 2, 10]
val map: Map<String, String> = mapOf("a" to "x", "b" to "y", "c" to "z").withDefault { "default" }
val result1: Set<Map.Entry<String, String>> = map.entries // [a=x, b=y, c=z]
val result2: Set<String> = map.keys // [a, b, c]
val result3: Collection<String> = map.values // [x, y, z]
val result4: String? = map["d"] // null
val result5: String = map.getValue("d") // "default"
val result6: String = map.getOrDefault("d", "not found") // not found
val result7: String = map.getOrElse("d") { "not found" } // not found
val map: Map<Char, Int> = mapOf('a' to 10, 'b' to 20)
val result1: Map<Char, Int> = map.mapKeys { it.key.uppercaseChar() } // {A=10, B=20}
val result2: Map<Char, Int> = map.mapValues { it.value * 10 } // {a=100, b=200}
val result3: Map<Char, Int> = map.map { it.key.uppercaseChar() to it.value * 10 }.toMap() // {A=100, B=200}
val map: Map<Char, Char> = mapOf('A' to 'a', 'B' to 'b')
val result1: Map<Char, Char> = map.filterKeys { it == 'A' } // {A=a}
val result2: Map<Char, Char> = map.filterValues { it == 'b' } // {B=b}
val result3: Map<Char, Char> = map.filter { it.key == 'B' && it.value == 'b' } // {B=b}
val map1: Map<Char, Char> = mapOf('A' to 'a')
val map2: Map<Char, Char> = mapOf('B' to 'b')
val maps: List<Map<Char, Char>> = listOf(map1, map2)
val map: Map<Char, Char> = maps.reduce { acc, x -> acc + x } // {A=a, B=b}
println(sortedMapOf("10" to "ten", "1" to "one", "2" to "two")) // {1=one, 10=ten, 2=two}
println(sortedMapOf(comparator = reverseOrder(), "10" to "ten", "1" to "one", "2" to "two")) // {2=two, 10=ten, 1=one}
println(sortedMapOf(comparator = compareBy { it.toInt() }, "10" to "ten", "1" to "one", "2" to "two")) // {1=one, 2=two, 10=ten}
val map = mapOf("ten" to "10", "one" to "1", "two" to "2")
println(map.toList().sortedBy { it.second }.toMap()) // {one=1, ten=10, two=2}
println(map.toList().sortedByDescending { it.second }.toMap()) // {two=2, ten=10, one=1}
println(map.toList().sortedBy { it.second.toInt() }.toMap()) // {one=1, two=2, ten=10}
val map = mapOf("10" to "ten", "1" to "one", "2" to "two")
println(map.toSortedMap()) // {1=one, 10=ten, 2=two}
println(map.toSortedMap(comparator = reverseOrder())) // {2=two, 10=ten, 1=one}
println(map.toSortedMap(comparator = compareBy { it.toInt() })) // {1=one, 2=two, 10=ten}
val x: String? = null
val y: String = x.orEmpty()
val a: Boolean = " ".isBlank() // true
val b: Boolean = "".isEmpty() // true
val c: String = " ".ifBlank { "default" } // "default"
val d: String = "".ifEmpty { "default" } // "default"
val s1: String = "a/b/c".substringBefore("/") // a
val s2: String = "a/b/c".substringAfter("/") // b/c
val s3: String = "a/b/c".substringBeforeLast("/") // a/b
val s4: String = "a/b/c".substringAfterLast("/") // c
val s1: String = "abc".removePrefix("a") // bc
val s2: String = "abc".removeSuffix("c") // ab
val s3: String = "aaa".removeSurrounding("a") // a
val s4: String = "abc".removeSurrounding("a", "c") // b
val xs: String = listOf("a", "b", "c").joinToString(
separator = ";",
prefix = "{",
postfix = "}",
limit = 2,
truncated = "..."
) {
it.uppercaseChar()
} // "{A;B;...}
val s: String = buildString {
appendLine("aaa")
appendLine("bbb")
}
enum class MyColor(val value: Int) {
BLACK(0x000000), WHITE(0xffffff), UNKNOWN(Int.MIN_VALUE);
companion object {
// Verbose for illustrative purposes
fun fromOrdinal(ordinal: Int): MyColor = entries[ordinal]
fun fromOrdinalOrNull(ordinal: Int): MyColor? = entries.getOrNull(ordinal)
fun fromOrdinalOrDefault(ordinal: Int): MyColor = entries.getOrElse(ordinal) { UNKNOWN }
fun fromName(name: String): MyColor = valueOf(name)
fun fromNameIgnoreCase(name: String): MyColor = valueOf(name.uppercase())
fun fromNameOrNull(name: String): MyColor? = entries.find { it.name == name }
fun fromNameIgnoreCaseOrNull(name: String): MyColor? = entries.find { it.name == name.uppercase() }
fun fromValue(value: Int): MyColor = entries.first { it.value == value }
fun fromValueOrNull(value: Int): MyColor? = entries.find { it.value == value }
}
}
enum class Fruit {
APPLE {
override fun toString() = "π"
override val producer: String = "π¨βπΎ"
override fun printSimilarFruit() {
println("π")
}
},
ORANGE {
override fun toString() = "π"
override val producer: String = "π©βπΎ"
override fun printSimilarFruit() {
println("π")
}
};
abstract val producer: String
abstract fun printSimilarFruit()
}
println(apple) // π
println(apple.producer) // π¨βπΎ
apple.printSimilarFruit() // π
enum class Fruit(val emoji: String) {
APPLE("π"),
ORANGE("π");
}
enum class ExtendedFruit(emoji: String) {
APPLE(Fruit.APPLE.emoji),
ORANGE(Fruit.ORANGE.emoji),
BANANA("π");
}
fun main() {
val xs: List<Int> = listOf(1, 2)
// Function reference
val result1: List<Int> = xs.filter(::isOdd) // [true, false]
// Qualified function reference
val result2: List<String> = xs.map(Int::toString) // ["1", "2"]
// Companion object function reference
val result3: List<Int> = xs.map((Sample)::double) // [2, 4]
val result4: List<Int> = xs.map(Sample.Companion::double) // [2, 4]
// Constructor reference
val result5: List<Sample> = xs.map(::Sample) // [Sample(1), Sample(2)]
}
fun isOdd(x: Int): Boolean = x % 2 != 0
data class Sample(val x: Int) {
companion object {
fun double(x: Int): Int {
return x * 2
}
}
}
Prefer sealed interfaces to sealed classes, except in these two cases.
Using the constructor of a sealed class is more concise than ...
sealed class Fruit(val emoji: String)
data class Apple(val foo: String) : Fruit(emoji = "π")
data class Orange(val bar: String) : Fruit(emoji = "π")
... using a sealed interface.
sealed interface Fruit {
val emoji: String
}
data class Apple(val foo: String, override val emoji: String = "π") : Fruit
data class Orange(val bar: String, override val emoji: String = "π") : Fruit
You can use generics with a sealed class, as shown below.
sealed class UiState<out T> {
data object Loading : UiState<Nothing>()
data class Success<T>(val data: T) : UiState<T>()
data class Failure(val throwable: Throwable) : UiState<Nothing>()
}
On the other hand, you cannot use generics with a sealed interface as shown below.
private sealed interface NonGenericUiState {
data object Loading : NonGenericUiState
data class Success(val data: String) : NonGenericUiState
data class Failure(val throwable: Throwable) : NonGenericUiState
}
enables you to use is
and as
on T
.
inline fun <reified T> f(a: Any) {
if (a is T) {
// Do something
}
val t = a as T
}
https://kotlinlang.org/docs/reference/inline-functions.html#reified-type-parameters
// The type can be omitted.
var observed: String by Delegates.observable("a") { _, old, new ->
if (old == new) return@observable // In case you only want to react to the value change.
println("$old -> $new")
}
println(observed) // "a"
observed = "b" // "a -> b"
observed = "b" // (none)
observed = "c" // "b -> c"
// The type can be omitted.
var evenNumber: Int by Delegates.vetoable(1) { _, old, new ->
new % 2 == 0
}
println(evenNumber) // 1. Though 1 is not an even number, the initial value is not validated.
evenNumber = 3 // vetoed because 3 is not a even number.
println(evenNumber) // 1
evenNumber = 2
println(evenNumber) // 2
Β | lazy | Delegates.notNull() | lateinit |
---|---|---|---|
val | supported | not supported | not supported |
non-nullable | supported | supported | not supported primitive types |
β»1) | good Dagger | supported | supported(β»2) |
- (β»1) A delegate object is created.
- (β»2) Dagger's field injection is not supported but Dagger's method (setter) injection is.
For pattern syntax reference see Pattern.
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/-regex/
fun sample() {
println(object {}.javaClass.enclosingMethod?.name) // sample
}
var x = ""
get() {
println("${object {}.javaClass.enclosingMethod?.name}: $field")
return field
}
set(value) {
println("${object {}.javaClass.enclosingMethod?.name}: $value")
field = value
}
println(Thread.currentThread().name)
// alternatively
println(currentCoroutineContext())
- The preceding code also shows the name of the current coroutine after the following preparation:
- IntelliJ IDEA > Menu bar > Run > Edit Configurations > Configurations > VM options >
add
-Dkotlinx.coroutines.debug
.
- IntelliJ IDEA > Menu bar > Run > Edit Configurations > Configurations > VM options >
add
Function | Throws |
---|---|
check(Boolean) | IllegalStateException |
checkNotNull(T?) | IllegalStateException |
error(Any) | IllegalStateException |
require(Boolean) | IllegalArgumentException |
requireNotNull(T?) | IllegalArgumentException |
Β | Type of initial value | Type of element of input array | Return type |
---|---|---|---|
fold | R | T | R |
reduce | n/a | T | T or one of T's ancestors |
- https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/fold.html
- https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/reduce.html
/**
* @see <a href="https://example.com">Example</a>
*/
Markdown also works. However, Intellij IDEA issues a warning that "Example" in [Example]
is invalid.
/**
* @see [Example](https://example.com)
*/