Featured image of post Coding Tip:使用先决条件而非嵌套 if

Coding Tip:使用先决条件而非嵌套 if

别嵌套 if 啦!!!

对比以下代码:

fun nested() {
  if (condition1) {
    doThings1()
    if (condition2) {
      doThings2()
      if (condition3) {
        doThings3()
      } else {
        throw Exception3()
      }
    } else {
      throw Exception2()
    }
  } else {
    throw Exception1()
  }
}
fun preconditions() {
  require(condition1)
  require(condition2)
  require(condition3)
  doThings1()
  doThings2()
  doThings3()
}

后者的风格显著提高了可读性。

你可以直接使用 Kotlin 标准库中提供的函数。在 Preconditions.kt 中你可以看到一系列先决条件工具方法。

只有一个 value 参数的 require check,以及具有 lazyMessage 的变体。同样也有 checkNotNull requireNotNull 变体。

requirecheck 的区别不大,在输入的条件为 false 时,前者抛出 IllegalArgumentException,后者抛出 IllegalStateException

@Sample
fun failRequireWithLazyMessage() {

  fun getIndices(count: Int): List<Int> {
    require(count >= 0) { "Count must be non-negative, was $count" }
    // ...
    return List(count) { it + 1 }
  }

  assertFailsWith<IllegalArgumentException> { getIndices(-1) }

  assertPrints(getIndices(3), "[1, 2, 3]")
}

@Sample
fun failCheckWithLazyMessage() {

  var someState: String? = null
  fun getStateValue(): String {
    val state = checkNotNull(someState) { "State must be set beforehand" }
    check(state.isNotEmpty()) { "State must be non-empty" }
    // ...
    return state
  }

  assertFailsWith<IllegalStateException> { getStateValue() }

  someState = ""
  assertFailsWith<IllegalStateException> { getStateValue() }

  someState = "non-empty-state"
  assertPrints(getStateValue(), "non-empty-state")
}

同时 error 函数也很有用,他是 throw IllegalStateException 的缩写。

fun failWithError() {
  val name: String? = null

  assertFailsWith<IllegalStateException> { val nonNullName = name ?: error("Name is missing") }
}

上面的示例来自 Kotlin 官方仓库

如果不想抛出异常:

fun List<Int>.getElement(x: Int): Int? {
  if (x !in 0..(size - 1)) return null
  return get(x)
}

fun File.touch() {
  if (exist()) return
  file.createNewFile()
}

@Test
fun lambdaFastReturn() {
  listOf(0, 1, 2, 3, 5, -1).forEach {
    if (it < 0) return@forEach
    assert(it >= 0)
  }
}
comments powered by Disqus