鼓励使用Kotlin, 鼓励使用新特性, 提倡Kotlin严格Review
代码规约 Coding Conventions
https://kotlinlang.org/docs/reference/coding-conventions.html
代码样例 kotlin Idiomatic
https://kotlinlang.org/docs/reference/idioms.html
Github中搜一下 kotlin Idiomatic 试试?
可能遇到的比较大的坑
代码规范 or Bad Case【持续添加】
- 判空
nullable?.let{
//非空操作
}
//涉及bool
if(nullable?.isTrue == true) {
}
- evils 的作用范围
val value = if(matches()) nullable :? 1 else nullable ?:2
val value = (if (matches()) nullable1 else nullable2) ?: 0
- 拒绝强制非空
//禁止出现!!强制非空操作
var value = InstanceManager.getInstance()!!.value
- 合理使用 run,let,with,apply 等
// Java 风格
fun trackFeedItemRemoved(concernId: Long, groupId: Long) {
val params = JSONObject()
params.putOpt("concern_id", concernId)
params.putOpt("group_id", groupId)
AppLogNewUtils.onEventV3("concern_topic_article_remove", params)
}
// 使用Apply
fun trackDescriptionRichContentClick(concernId: Long, text: String?) {
Bundle().apply {
putLong("concern_id", concernId)
putString("text", text)
}.let {//可以用run或者apply吗? 逻辑上可以用,单从代码语义上不建议**
AppLogNewUtils.onEventV3Bundle("ugc_topic_click", **it**)
}
}
- 单例
`// Object生成的是线程安全的饿汉式单例`
`// Lazy 实现线程安全的懒汉式单例`
`class SingletonDemo private constructor() { `
`companion object {`
`val instance: SingletonDemo by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {`
` SingletonDemo() `
` } `
` } `
`}`
- kotlin可以省略Builder模式
`// Java Builder 风格的例子`
`val builder = PreLayoutTextViewConfig.builder()`
` .textViewWidth(PostTextLayoutProvider.INSTANCE.getWidthInPixel())`
` .textSize(PostTextLayoutProvider.INSTANCE.getTextSizeInPixel().toInt())`
` .textContent(content)`
` .textRichContentStr(commentBase.content_rich_span)`
` .ellipsizeContent(TTRichTextViewConfig.*ELLIPSIZE_ALL*)`
` .ellipsizeClickLength(2)`
` .maxLineCount(maxLine)`
` .defaultLineCount(defaultLine)`
`val interceptor = PostSpanInterceptor()`
`interceptor.cellRef = postCell`
`builder.interceptor(interceptor)`
`return builder.build()`
`//直接kotlin 可以省略build`
`//如果有需要从Java中调用还得写Buidler模式...`
`val dataSource = BasicDataSource().apply **{**`
`driverClassName = "com.mysql.jdbc.Driver"`
`url = "jdbc:mysql://domain:3309/db"`
`username = "username"`
`password = "password"`
`maxTotal = 40`
`maxIdle = 40`
`minIdle = 4`
`**}**`
- 代码可读性
`// 随便写的一些代码`
`// 用了很多kotlin函数省略了一些临时变量,看上去很高端,但是代码的可读性很差`
`fun test(context: Context) {`
`*with*(View.inflate(context, R.layout.*abc*, null) as ViewGroup) **{**`
`*repeat*(*childCount*) **{**`
`*with*(getChildAt(**it**) as Button) **{**`
`*apply* **{**`
`*text* = "button$**it**"`
`setOnClickListener **{**`
`*takeIf* **{ it** == getChildAt(0) **}**?.*run* **{**`
`doClickTopButton()`
`**}**?.let **{**`
`doClickOthers()`
`**}**`
`** }**`
`** }**`
`** }**`
`** }**`
`** }**`
`}`
- 不安全的类型转换
`// findViewById 是nullable`
`val readCountTv: TextView = itemView.findViewById(R.id.*read_count*) as TextView`
`val readCountWrapper: LinearLayout = itemView.findViewById(R.id.*read_count_wrapper*) as LinearLayout`
- kotlin 调用 Java 时, 属性调用可能调用Java的get方法, 有些场景会有隐藏的坑
`override fun onBackPressed() {`
`//此处videoController 实际调用的是下面Java代码的getVideoController()`
`//当videoController 为空时,反而会去创建一个新的实例`
`if (videoController?.isFullScreen == true && videoController.onBackPressed(this)) {`
`return`
` }`
` finish()`
`}`
`@Override`
`public IFeedVideoController getVideoController() {`
if (mVideoController == null) {
mVideoController = createVideoController()
initVideoListener();
if (mVideoController != null) {
mVideoController.addListener(mIVideoFullscreen);
}
}
return mVideoController;
`}`
kotlin构造函数 AS中show kotlin bytecode decode生成的java和最终打包生成的class不一致
用kotlinc 编译了下,目前看来是一致的
- kotlin中实现if (not) null
`//空和非空都有处理时,建议还是使用Java风格的写法 [kotlin if null 怎么写更优雅](https://bytedance.feishu.cn/docs/doccnlAPdp8LyMvHrVnYj9yRb5d) `
`var variable :Any? = null `
`if(variable != null) {`
` doSomeThing()`
`} else {`
` doSomeThingElse()`
`}`
- SDK项目中减少使用data class
`//主要是因为data支持解构,业务方如果是解构的写法,sdk升级时如果不全量编译可能有问题`
`//参考 https://jakewharton.com/public-api-challenges-in-kotlin/`
- Kotlin编译后的隐式import(Kotlin的bug?),这样可能会导致增量编译的包有问题
`//从字节码看有IRelationStateCallback的依赖`
`//实际Kotlin代码中不会存在对RelationStatusCallback的直接依赖`
`byte var8;`
`label16: {`
` IRelationDepend var10000 = (IRelationDepend)ServiceManager2.getService (IRelationDepend.class);`
` if (var10000 != null) {`
`if (var10000.userIsFollowing(userId, (RelationStatusCallback)null)) {`
`var8 = 1;`
` break label16;`
` }`
` }`
`var8 = 0;`
`}`