Kotlin 以其简洁实用的语法,赢得了很多Java 开发者,尤其是 Android 开发者的喜爱与应用。然而,虽然我们使用 Kotlin 进行编码,可能并没有书写出地道的 Kotlin 代码,亦或者是遵照写Java的思维,用Kotlin的语法 来编码。
本文将通过多出代码示例,分为Do not
(不建议)和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-else
,when
,try-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 } |
善用 apply
/also
/with
//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() |
使用方法引用
data class NewsItem(val content: String, val isFake: Boolean)
//Do not fun normalLambda() { arrayOf<NewsItem>().filter { it.isFake }.let { print(it) } } //Do 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的代码示例,如有补充,请在下方评论指出。谢谢。
相关阅读
- 研究学习Kotlin的一些方法
- Kotlin 中的 Lambda 与 inline
- 有点意思的Kotlin的默认参数与JVMOverloads