Kotlin - Idioms

XYMON·2023년 4월 10일
0

코틀린

목록 보기
2/7

official docs

A collection of random and frequently used idioms in Kotlin.

Create DTOs(POJOs/POCOs)

data class Customer(val name: String, val email: String)

POJO - Plain Old Java Object
POCO - Plain Old CLR(Common Language Runtime) Object

These terms are used to describe simple, lightweight classes that are used to represent data in an application.

Theses methods are automatically generated.

  • Getter/Setter: The 'name' and 'email' properties - accessing and modifying.
  • 'equals()': compares two instacnces of 'Customer' class for equality based on their property values.
  • 'hashCode()': returns a hash code value for the 'Customer' instance based on its property values.
  • 'toString()': returns a string representation of the 'Customer' instance based on its property values.
  • 'copy()': creates a new 'Customer' instance with the same property values.
  • 'component1()','componet2()': allow destructuring of Customer instance into its individual properties.
    ex.
val customer = Customer("John", "john@example.com")
val (name, email) = customer

Default values for function parameters

fun foo(a: Int = 0, b: String = "") { ... }

Filter a list

val positives = list.filter { x -> x > 0 }
val positives = list.filter { it > 0 }

Check the presence of an element

if ("john@example.com" in emailsList) { ... }
if ("jane@example.com" !in emailsList) { ... }

String interpolation

println("Name $name")

Instance checks

when (x) {
    is Foo -> ...
    is Bar -> ...
    else   -> ...
}

Read-only list/map

val list = listOf("a", "b", "c")
val map = mapOf("a" to 1, "b" to 2, "c" to 3)

// Access
println(map["key"])
map["key"] = value

// Traverse
for ((k, v) in map) {
    println("$k -> $v")
}

Iterate over a range

// closed range: includes 100
for (i in 1..100) { ... }  
// half-open range: does not include 100
for (i in 1 until 100) { ... } 
for (x in 2..10 step 2) { ... }
for (x in 10 downTo 1) { ... }
(1..10).forEach { ... }

Lazy property

// the value is computed only on first access
val p: String by lazy { 
    // compute the string
}

Extension functions

fun String.spaceToCamelCase() { ... }

"Convert this to camelcase".spaceToCamelCase()

Create a singleton

object Resource {
    val name = "Name"
}

In kotlin, 'object' declares a sigleton which is a class that can have only one instance.

Instantiate an abstract class

abstract class MyAbstractClass {
    abstract fun doSomething()
    abstract fun sleep()
}

fun main() {
    val myObject = object : MyAbstractClass() {
        override fun doSomething() {
            // ...
        }

        override fun sleep() { // ...
        }
    }
    myObject.doSomething()
}

If-not-null shorthand

val files = File("Test").listFiles()

println(files?.size) 
// size is printed if files is not null

//If-not-null-else 
println(files?.size ?: "empty") 
// if files is null, this prints "empty"

// To calculate the fallback value in a code block, use `run`
val filesSize = files?.size ?: run {
    return someSize
}
println(filesSize)

'?.'- safe call operator : if the value on the left is null, this expression returns null.
'?:' - Elvis operator : if the value on the left is null, this expression provides an alternative value.

Execute a statement if null

val values = ...
val email = values["email"] ?: throw IllegalStateException("Email is missing!")

Get first item of a possibly empty collection

val emails = ... // might be empty
val mainEmail = emails.firstOrNull() ?: ""

Execute if not null

val value = ...

value?.let {
    ... // execute this block if not null
}

Map nullable value if not null

val value = ...

val mapped = value?.let { transformValue(it) } ?: defaultValue
// defaultValue is returned if the value or the transform result is null.

Return on when statement

fun transform(color: String): Int {
    return when (color) {
        "Red" -> 0
        "Green" -> 1
        "Blue" -> 2
        else -> throw IllegalArgumentException("Invalid color param value")
    }
}

try-catch expression

fun test() {
    val result = try {
        count()
    } catch (e: ArithmeticException) {
        throw IllegalStateException(e)
    }

    // Working with result
}

if expression

val y = if (x == 1) {
    "one"
} else if (x == 2) {
    "two"
} else {
    "other"
}

// when is also possible
val y = when (x) {
    1 -> "one"
    2 -> "two"
    else -> "other"
}

Builder-style usage of methods that return Unit

fun arrayOfMinusOnes(size: Int): IntArray {
    return IntArray(size).apply { fill(-1) }
}

'apply' function is a useful tool for creating builder-style APIs that allow chaining of method calls, even if the methods return 'Unit'.

  • allows subsequent method calls to be chained together in a single expression.
val myArray = arrayOfMinusOnes(5)

Like this, myArray is created by just a single expression.

Single-expression functions

fun theAnswer() = 42
// this is equivalent to

fun theAnswer(): Int {
    return 42
}
//or like this

val y = when (x) {
    1 -> "one"
    2 -> "two"
    else -> "other"
}

Call multiple methods on an object instance (with)

class Turtle {
    fun penDown()
    fun penUp()
    fun turn(degrees: Double)
    fun forward(pixels: Double)
}

val myTurtle = Turtle()
with(myTurtle) { //draw a 100 pix square
    penDown()
    for (i in 1..4) {
        forward(100.0)
        turn(90.0)
    }
    penUp()
}

Configure properties of an object (apply)

val stream = Files.newInputStream(Paths.get("/some/file.txt"))
stream.buffered().reader().use { reader ->
    println(reader.readText())
}

The try-with-resources feature introduced in Java 7 allows you to write more concise and robust code when dealing with resources.

'use' function creates new scope and the input stream is automatically closed when the scope is exited.

Within 'use', stream -> buffered stream with 'bueffered()' and buffered -> reader with 'reader()'. And it is passed to a lamda expression which uses readText().

The use function ensures that the input stream and the reader are closed after the lambda expression is executed, even if an exception is thrown.

Generic function that requires the generic type information

//  public final class Gson {
//     ...
//     public <T> T fromJson(JsonElement json, Class<T> classOfT) throws JsonSyntaxException {
//     ...

inline fun <reified T: Any> Gson.fromJson(json: JsonElement): T = this.fromJson(json, T::class.java)

//for ex
val json = "{\"name\": \"John\", \"age\": 30}"
val gson = Gson()
val person = gson.fromJson<Person>(json)

Nullable Boolean

val b: Boolean? = ...
if (b == true) {
    ...
} else {
    // `b` is false or null
}

Swap two variables

var a = 1
var b = 2
a = b.also { b = a }

'also' is simmilar to 'apply'.
Diff is that 'also' returns the object itself, while 'apply' returns the result of the lamda expression.

TODO

fun calcTaxes(): BigDecimal = TODO("Waiting for feedback from accounting")

Kotlin's standard library has a TODO() function that will always throw a NotImplementedError

profile
염염

0개의 댓글