编写地道的 Kotlin 代码(结尾有福利)

Kotlin 以其简洁实用的语法,赢得了很多Java 开发者,尤其是 Android 开发者的喜爱与应用。然而,虽然我们使用 Kotlin 进行编码,可能并没有书写出地道的 Kotlin 代码,亦或者是遵照写Java的思维,用Kotlin的语法 来编码。

本文将通过多出代码示例,分为 Donot(不建议)和 Do(建议)两部分,分别代表着不太好的实现和推荐的实现方式,来展示地道的 Kotlin 编码方式。

进行非null判断

//Do not	
fun dumpBook(book: Book?) {	
    if (book != null) {	
        book.dumpContent()	
    }	
}	
//Do	
fun dumpBook1(book: Book?) {	
    book?.dumpContent()	
}

进行类型转换并访问一些属性

// 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)	
    }	
}

避免使用 !!非空断言

//Do not	
fun testNotNullAssertion(feed: Feed) {	
    feed.feedItemList.first().author!!.title	
}	
//Do	
fun testNotNullAssertion0(feed: Feed) {	
    feed.feedItemList.first().author?.title ?: "fallback_author_title"	
}

补充:

  • 使用 !!断言,一旦断言条件出错,会发生运行时异常。

判断可能为null的boolean值

// 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	
    }	
}

利用 if-elsewhentry-catch 的返回值

//Do not	
fun testIfElse(success: Boolean) {	
    var message: String	
    if (success) {	
        message = "恭喜,成功了"	
    } else {	
        message = "再接再厉"	
    }	
    println(message)	
}	
//Do	
fun testIfElse1(success: Boolean) {	
    val message = if (success)  {	
        "恭喜,成功了"	
    } else {	
        "再接再厉"	
    }	
}	
//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	
}

善用 applyalsowith

//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)	
    }	
}
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	
    }	
}
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()	
    }	
}

直接使用top-level方法,而不是Object里的方法

//Do not	
object AppUtil {	
    fun isAppEnabled(packageName: String): Boolean {	
        TODO()	
    }	
}	
//Do	
//AppUtil.kt file	
fun isAppEnabled(packageName: String): Boolean {	
    TODO()	
}

使用Kotlin的默认参数特性,而不是方法重载

//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)	
}

优先定义并使用扩展方法,而不是Util方法

//Do not	
fun isStringPhoneNumber(value: String): Boolean {	
    TODO()	
}	
//Do	
fun String.isPhoneNumber(): Boolean = TODO()

使用方法引用

//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)	
}

使用inline修饰高阶函数(参数为函数时)

//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()	
    }	
}

备注:

  • 关于inline的问题,可以参考Kotlin 中的 Lambda 与 Inline

把函数参数尽可能放到最后

//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")	
    }	
}

使用mapNotNull

//Do not	
fun testMapNotNull(list: List<FeedItem>) {	
    list.map { it.author }.filterNotNull()	
}	
//Do	
fun testMapNotNull0(list: List<FeedItem>) {	
    list.mapNotNull { it.author }	
}

尽可能使用只读集合

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)	
}

适宜情况下使用 Pair或 Triple

// 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秒")	
}

使用lazy 替代繁琐的延迟初始化

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()	
    }	
}

使用lateinit 处理无法再构造函数初始化的变量

//Do not	
class FeedItem {	
    var author: Feed.Author? = null	
}	
//Do	
class FeedItem0 {	
    lateinit var author: Feed.Author	
}

善用Data class的copy方法

//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")	
}

针对函数类型和集合使用 typealias

//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"))	
}

使用含义更加清晰的 substringBefore和 substringAfter

//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("|"))	
}

以上就是一些相对更加Kotlin style的代码示例,如有补充,请在下方评论指出。谢谢。

第二波送书来了,点击阅读原文即可参与。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值