本文引用自大神的博客
https://droidyue.com/blog/2019/05/19/do-and-dont-in-kotlin/
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)
}
备注:
- 具体为什么要这样用,可以移步另外一篇文章
https://www.jianshu.com/writer#/notebooks/37986998/notes/69778088/preview
使用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))
}
//Do
fun testSubstring0() {
val message = "user|password"
Log.i("testSubstring.user=", message.substringBefore("|"))
Log.i("testSubstring.password=", message.substringAfter("|"))
}
网友评论