Kotlin has won over many Java developers, especially Android developers, with its concise and practical syntax. However, while we may be coding in Kotlin, we may not be writing Kotlin native code, or we may be coding in Kotlin’s syntax in the same way we wrote Java.

This article will demonstrate authentic Kotlin coding with additional code examples, divided into Do Not (not recommended) and Do (recommended) sections, which represent the bad implementation and the recommended implementation, respectively.

Perform a non-NULL judgment


1
2
3
4
5
6
7
8
9
10
11
Copy the code
//Do not fun dumpBook(book: Book?) { if (book ! = null) { book.dumpContent() } } //Do fun dumpBook1(book: Book?) { book? .dumpContent() }Copy the code

Type conversion and access some properties


12 3 4 5 6 7 8 9 10 11 12 13 14Copy the code
// avoid if type checks //Do not fun testTypeCheck(any: Any) { if (any is Book) { println(any.isbn) } } //Do fun testTypeCheck0(any: Any) { (any as? Book)? .let { println(it.isbn) } }Copy the code

Avoid the use of!!!!!Not empty assertion


1, 2, 3, 4, 5, 6, 7, 8, 9Copy the code
//Do not fun testNotNullAssertion(feed: Feed) { feed.feedItemList.first().author!! .title } //Do fun testNotNullAssertion0(feed: Feed) { feed.feedItemList.first().author? .title ? : "fallback_author_title" }Copy the code

Supplement:

  • use!!!!!Assertion, a runtime exception occurs when the assertion condition is wrong.

Determine Boolean values that may be NULL


12 3 4 5 6 7 8 9 10 11 12 13 14 15 16Copy the code
// Do not fun comsumeNullableBoolean() { var isOK: Boolean? = null if (isOK ! = null && isOK) { //do something } } //Do fun comsumeNullableBoolean0() { var isOK: Boolean? = null if (isOK == true) { //do something } }Copy the code

usingif-else.when.try-catchThe return value of the


12 34 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42Copy the code
//Do not fun testIfElse(success: Boolean) { var message: String if (success) {message = "congratulations, success"} else {message = "further"} println(message)} //Do fun testIfElse1(success: Boolean) {val message = if (success) {else {" try again "}} //Do fun testWhen0(type: Int) { val typeString = when(type) { 1 -> "post" 2 -> "status" else -> "page" } //can't reassign value to typeString } fun getWebContent(url: String): String = TODO() //Do fun testTryCatch() { val content = try { getWebContent("https://droidyue.com") } catch(e: IOException) {null} //can't reassign value to content}Copy the code

Make the best useapply/also/with


12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17Copy the code
//Do not
fun composeIntent(): Intent {
    val intent = Intent(Intent.ACTION_VIEW)
    intent.data = Uri.parse("https://droidyue.com")
    intent.`package` = "com.android.chrome"
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
    return intent
}

//Do
fun composeIntent1(): Intent {
    return Intent(Intent.ACTION_VIEW).apply {
        data = Uri.parse("https://droidyue.com")
        `package` = "com.android.chrome"
        addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
    }
}
Copy the code

12 34 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34Copy the code
data class Request(val uri: String) //use also //Do not fun handleRequest(request: Request) : Boolean { return when { request.uri.startsWith("https") -> { handleHttpsRequest(request) true } request.uri.startsWith("http") -> { handleHttpRequest(request) true } else -> false } } //Do fun handleRequest1(request:  Request): Boolean { return when { request.uri.startsWith("https") -> true.also { handleHttpsRequest(request) } request.uri.startsWith("http") -> true.also { handleHttpRequest(request) } else -> false } }Copy the code

12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25Copy the code
class Navigator {
    fun turnLeft() = Unit
    fun turnRight() = Unit
    fun forward() = Unit
    fun backward() = Unit
}

//use with
//Do not
fun navigate(navigator: Navigator) {
    navigator.forward()
    navigator.turnRight()
    navigator.backward()
    navigator.turnLeft()
}

//Do
fun navigate1(navigator: Navigator) {
    with(navigator) {
        forward()
        turnRight()
        backward()
        turnLeft()
    }
}
Copy the code

Use top-level methods directly instead of Object methods


12 3 4 5 6 7 8 9 10 11 12Copy the code
//Do not
object AppUtil {
    fun isAppEnabled(packageName: String): Boolean {
        TODO()
    }
}

//Do
//AppUtil.kt file
fun isAppEnabled(packageName: String): Boolean {
    TODO()
}
Copy the code

Use Kotlin’s default parameter feature instead of method overloading


12 3 4 5 6 7 8 9 10 11 12 13Copy the code
//Do not
class BadPizza {
    constructor(size: Float)

    constructor(size: Float, hasCheese: Boolean)

    constructor(size: Float, hasCheese: Boolean, hasBacon: Boolean)
}

//Do
class GoodPizza {
    constructor(size: Float, hasCheese: Boolean = false, hasBacon: Boolean = false)
}
Copy the code

Define and use extension methods in preference to Util methods


One, two, three, four, five, six, sevenCopy the code
//Do not
fun isStringPhoneNumber(value: String): Boolean {
    TODO()
}

//Do
fun String.isPhoneNumber(): Boolean = TODO()
Copy the code

Use method reference


1
2
3
4
5
6
7
8
9
10
11
Copy the code
//Do not

data class NewsItem(val content: String, val isFake: Boolean)

fun normalLambda() {
    arrayOf<NewsItem>().filter { it.isFake }.let { print(it) }
}

fun methodReference() {
    arrayOf<NewsItem>().filter(NewsItem::isFake).let(::print)
}
Copy the code

Use inline to modify higher-order functions (when arguments are functions)


12 3 4 5 6 7 8 9 10 11 12 13 14 15 16Copy the code
//Do not
fun safeRun(block: () -> Unit) {
    try {
        block()
    } catch (t: Throwable) {
        t.printStackTrace()
    }
}
//Do
inline fun safeRun0(block: () -> Unit) {
    try {
        block()
    } catch (t: Throwable) {
        t.printStackTrace()
    }
}
Copy the code

Remark:

  • For inline problems, see Lambda and inline in Kotlin

Put function arguments as last as possible


12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19Copy the code
//Do not
fun delayTask(task: () -> Unit, delayInMillSecond: Long)  {
    TODO()
}

//Do 
fun delayTask0(delayInMillSecond: Long, task: () -> Unit) {
    TODO()
}

fun testDelayTasks() {
    delayTask({
        println("printing")
    }, 5000L)

    delayTask0(5000L) {
        println("printing")
    }
}
Copy the code

Using mapNotNull


1, 2, 3, 4, 5, 6, 7, 8, 9Copy the code
//Do not
fun testMapNotNull(list: List<FeedItem>) {
    list.map { it.author }.filterNotNull()
}

//Do
fun testMapNotNull0(list: List<FeedItem>) {
    list.mapNotNull { it.author }
}
Copy the code

Use read-only collections whenever possible


12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19Copy the code
fun parseArguments(arguments: Map<String, String>) { //do some bad things //try to clear if the argument is available to be cleared. (arguments as? HashMap)? .clear() } //use read-only collections as much as possible //Do not fun useMutableCollections() { val arguments = hashMapOf<String, String>() arguments["key"] = "value" parseArguments(arguments) } //Do fun useReadOnlyCollections() { val arguments = mapOf("key" to "value") parseArguments(arguments) }Copy the code

Suitable for usePairorTriple


1, 2, 3, 4, 5, 6, 7, 8Copy the code
// Use Pair or Triple fun returnValues(): Pair<Int, String> { return Pair(404, "File Not Found") } fun returnTriple(): Triple<String, String, String> {return Triple("6 ", "6 ", "60 ")}Copy the code

Use lazy instead of tedious lazy initialization


12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22Copy the code
data class Config(val host: String, val port: Int)

fun loadConfigFromFile(): Config = TODO()

//Do not
object ConfigManager {
    var config: Config? = null

    fun getConfig0() : Config? {
        if (config == null) {
            config = loadConfigFromFile()
        }
        return config
    }
}

//Do
object ConfigManager1 {
    val config: Config by lazy {
        loadConfigFromFile()
    }
}
Copy the code

Use LateInit to handle variables that cannot be initialized by the constructor


1, 2, 3, 4, 5, 6, 7, 8, 9Copy the code
//Do not
class FeedItem {
    var author: Feed.Author? = null
}

//Do
class FeedItem0 {
    lateinit var author: Feed.Author
}
Copy the code

Make good use of the Data class copy method


12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25Copy the code
//Do not
class Car {
    private var engine: String? = null

    constructor(theEngine: String) {
        engine = theEngine
    }

    constructor(car: Car) {
        engine = car.engine
    }
}

//Do
data class Car0(val engine: String)


fun test() {
    val firstCar = Car("Honda")
    val secondCar = Car(firstCar)

    val thirdCar = Car0("Nissan")
    val fourthCar = thirdCar.copy()
    val fifthCar = thirdCar.copy(engine = "Ford")
}
Copy the code

For function types and collectionstypealias


12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19Copy the code
//Do not

interface OnValueChangedListener {
    fun onValueChanged(value: String)
}

//Do
typealias OnValueChangedListener0 = (String) -> Unit

val value : OnValueChangedListener0 = {
    println(it)
}

//Do
typealias BookSet = HashSet<Book>

val bookSet = BookSet().apply {
    add(Book("978-0131872486"))
}
Copy the code

Use something with a clearer meaningsubstringBeforeandsubstringAfter


12 3 4 5 6 7 8 9 10 11 12 13 14 15Copy the code
//Do not
fun testSubstring() {
    val message = "user|password"
    Log.i("testSubstring.user=", message.substring(0, message.indexOf("|")))

    Log.i("testSubstring.password=", message.substring(message.indexOf("|") + 1))
}


fun testSubstring0() {
    val message = "user|password"
    Log.i("testSubstring.user=", message.substringBefore("|"))

    Log.i("testSubstring.password=", message.substringAfter("|"))
}
Copy the code

These are some of the more Kotlin style code examples, please point them out in the comments below. thank you

reading

  • Study some of Kotlin’s methods
  • Lambda and inline in Kotlin
  • Interesting Kotlin default parameter with JVMOverloads