注册

记录 Kotlin 实践的一些好建议

目录



  1. 注释
  2. 函数式接口
  3. 高阶函数
  4. 扩展函数

注释


Java:


    /**
    * @see AdVideoUserInfoContainerData#type
    */
   public Builder type(int type) {
       userInfoData.type = type;
       return this;
  }
   /** 事件配置, 对应于 {@link FeedAdLottieRepoInfo#name 属性} */
   public String lottieConfig;

Kotlin:


/**
* 由[CountDownType.type] mapTo [CountDownType]
* 避免了使用 when(type) 写 else 了
*/
private fun type2Enum(type: () -> String): CountDownType {
   return CountDownType.values().firstOrNull {
       it.type == type()
  } ?: CountDownType.CIRCLE
}

Kotlin 可以使用内联标记来引用类、方法、属性等,这比 Java 中的 @see、@link 更加易用。


文档:kotlinlang.org/docs/kotlin…


函数式接口


非函数式接口:


internal interface ICountDownCallback {
   /**
    * 倒计时完成时回调
    */
   fun finish()
}

internal fun setCountDownCallback(callback: ICountDownCallback) {
   // ignore
}

internal fun show() {
   setCountDownCallback(object : ICountDownCallback {
       override fun finish() {
           TODO("Not yet implemented")
      }
  })
}

函数式接口:


internal fun interface ICountDownCallback {
   /**
    * 倒计时完成时回调
    */
   fun finish()
}

internal fun setCountDownCallback(callback: ICountDownCallback) {
   // ignore
}

internal fun show() {
   setCountDownCallback {
       TODO("Not yet implemented")
  }
}

函数式接口也被称为单一抽象方法(SAM)接口,使用函数式接口可以使代码更加简洁,富有表现力。


对于 Java 的接口,比如 View.OnClickListener,它在使用的时候可以直接转 lambda 使用的,只有是 kotlin 的单一抽象方法,需要加 fun 关键字标示它为函数式接口。


文档:kotlinlang.org/docs/fun-in…


高阶函数


如果对象的初始化比较麻烦,可以使用高阶函数,让代码更加流畅:


    // 定义
open fun attachToViewGroup(
       viewGroup: ViewGroup,
       index: Int = -1,
       lp: () -> MarginLayoutParams = {
           MarginLayoutParams(
               LayoutParams.WRAP_CONTENT,
               LayoutParams.WRAP_CONTENT
          )
      }
  ) {
      (this.parent as? ViewGroup)?.removeView(this)
       viewGroup.addView(this, lp.invoke())
  }

// 使用
   override fun attachToViewGroup(viewGroup: ViewGroup, index: Int, lp: () -> MarginLayoutParams) {
       super.attachToViewGroup(viewGroup, index) {
           MarginLayoutParams(
               ViewGroup.LayoutParams.WRAP_CONTENT,
               ViewGroup.LayoutParams.WRAP_CONTENT
          ).apply {
               leftMargin = 14.px(context)
               topMargin = 44.px(context)
          }
      }
  }

如果参数的获取比较复杂,代码比较长,有不少判断逻辑,也可以使用高阶函数:


// 定义
fun getCountDownViewByType(context: Context, type: () -> String = { "0" }) {
// ignore
}
// 使用
countDownView = CountDownType.getCountDownViewByType(this) {
rewardVideoCmdData.cmdPolicyData?.countDownType ?: ""
}

如果方法的返回值是一个状态值,然后根据状态值去做相关逻辑处理。这种情况下,其实我们想要的是一个行为,比如代码中充斥着大量的数据解析、校验等逻辑,我们也可以是使用高阶函数重构:


// 重构之前
/**
* 校验数据有效(校验标题和按钮有一个不为空,就可以展示 Dialog)
*/
fun checkValid(): Boolean {
   return !dialogTitle.isNullOrEmpty() || !buttonList.isNullOrEmpty()
}

private fun bindData() {
   rewardData = RewardDialogData(arguments?.getString(EXTRA_REWARD_DATA) ?: "")
   // 弹窗数据不合法,就不需要展示 dialog 了
   if (rewardData == null || !rewardData!!.checkValid()) {
       dismiss()
       return
  }
   // 更新字体颜色等
   updateSkin()
}


// 重构之后
/**
* 数据校验失败,执行 [fail] 函数
*/
internal inline fun RewardDialogData?.checkFailed(fail: () -> Unit) {
   this?.let {
       if (dialogTitle.isNullOrEmpty() && buttonList.isNullOrEmpty()) {
           fail()
      }
  } ?: fail()
}


private fun bindData() {
   rewardData = RewardDialogData(arguments?.getString(EXTRA_REWARD_DATA) ?: "")
   // 弹窗数据不合法,就不需要展示 dialog 了
   rewardData?.checkFailed {
       dismiss()
       return
  }
   // 更新字体颜色等
   updateSkin()
}

kotlin 标准库里面也是有非常多的高阶函数的,比如作用域函数(let、apply、run等等),除此之外,还有一些集合类的标准库函数:


// filter
fun showCharge() {
   adMonitorUrl?.filter {
       !it.showUrl.isNullOrEmpty()
  }?.forEach {
       ParallelCharge.charge(it.showUrl)
  }
}
// forEachIndexed
list.forEachIndexed { index, i ->
// ignore
}

文档:kotlinlang.org/docs/lambda…


扩展函数


// 比较不流畅的写法
val topImgUrl = rewardData?.topImg
if (topImgUrl.isNullOrBlank()) {
   topImg.visibility = View.GONE
} else {
   topImg.hierarchy?.useGlobalColorFilter = false
   topImg.visibility = View.VISIBLE
   topImg.setImageURI(topImgUrl)
}

// 使用局部返回标签
topImg.apply {
   if (topImgUrl.isNullOrEmpty()) {
       visibility = View.GONE
       return@apply
  }
   hierarchy?.useGlobalColorFilter = false
   setImageURI(topImgUrl)
   visibility = View.VISIBLE
}

/**
* 校验 View 可见性
*
* @return [predicate] false: GONE;true: VISIBLE
*/
internal inline fun <reified T : View> T.checkVisible(predicate: () -> Boolean): T? {
   return if (predicate()) {
       visibility = View.VISIBLE
       this
  } else {
       visibility = View.GONE
       null
  }
}

// 使用扩展函数
topImg.checkVisible {
   !topImgUrl.isNullOrEmpty()
}?.run {
   hierarchy?.useGlobalColorFilter = false
   setImageURI(topImgUrl)
}

作者:Omooo
链接:https://juejin.cn/post/7029673754309427207
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册