【问题标题】:Best way to handle such scenario where "smart cast is imposible"处理“不可能进行智能投射”这种情况的最佳方法
【发布时间】:2018-05-23 17:43:28
【问题描述】:

我想知道处理这种情况的最佳方法是什么

class Person(var name:String? = null, var age:Int? = null){
    fun test(){
        if(name != null && age != null)
            doSth(name, age) //smart cast imposible
    }

    fun doSth (someValue:String, someValue2:Int){

    }
}

调用 doSth 方法并确保 name 和 age 为 nt null 的最简单方法是什么?

我正在寻找一些简单的东西,比如我会简单地使用 let 的可变场景

name?.let{ doSth(it) } 

【问题讨论】:

  • 姓名和年龄可变是否有特定原因?

标签: kotlin


【解决方案1】:

您可以随意嵌套let

fun test(){
    name?.let { name ->
        age?.let { age ->
            doSth(name, age) //smart cast imposible    
        }
    }
}

另一种可能更容易理解的方法是使用局部变量:

fun test(){
    val name = name
    val age = age
    if(name != null && age != null){
        doSth(name, age)
    }
}

最后但同样重要的是,考虑将Person 更改为不可变,如下所示:

data class Person(val name:String? = null, val age:Int? = null){
    fun test(){
        if(name != null && age != null){
            doSth(name, age)
        }
    }
    ...
}

【讨论】:

  • 感谢您的回答。所有提议的解决方案仍然感觉像“做简单工作的大量代码”,至少与 let 和单个变量相比 ;-/
  • @Panel 我有点像您所说的那样需要编写大量代码。虽然我必须说我有点喜欢这个事实,因为它鼓励人们使用不可变类型和只读属性。
  • 这点非常好,谢谢。虽然我不确定是否可以在任何地方应用它。来自服务器的数据
【解决方案2】:

为了使转换成为可能,您必须以某种方式制作该值的本地副本。在 Kotlin 中,最好明确地做到这一点:

val name = name
val age = age
if(name != null && age != null){
    doSth(name, age)
}

let 函数将其隐藏在抽象层后面,这不是最好的恕我直言。

【讨论】:

  • 不确定阴影变量是不是比let更好的方法
【解决方案3】:

有一个不错的小库,允许编写带有多个变量的let-like 代码。它是开源的,你可以在 GitHub 上找到它,它被称为 Unwrap

示例基于自述文件:

unwrap(_a, _b, _c) { a, b, c ->
    println("$a, $b$c") // all variables are not-null
}

所有unwrap(...) 方法都标记为inline,因此使用它们应该没有开销。

顺便说一下,这个库还允许处理一些空变量的情况(nah() 方法)。

【讨论】:

    【解决方案4】:

    如果你想让它有点“极端”,你可以在 Pair<String?,Int?> 上定义一个扩展函数,为你隐藏逻辑:

    fun Pair<String?,Int?>.test(block: (String, Int) -> Unit) {
        if(first != null && second != null) {
             block(first, second)
        }
    }
    

    那么,称呼它会更简洁一些

    (name to age).test { n, a ->
       println("name: $n age: $a")
    }
    

    但是,它不会真正帮助您(因为您也可以将其定义为 Person 类本身中的一个函数),除非您在整个项目中确实经常需要这种功能。就像我说的,这似乎有点矫枉过正。

    编辑 通过完全通用,您实际上可以使其(一点)更有用:

    fun <T,R> Pair<T?,R?>.ifBothNotNull(block: (T, R) -> Unit) {
        if(first != null && second != null){
             block(first, second)
        }
    }
    

    【讨论】:

    • 这会产生垃圾。带有 2 个参数的内联函数会更实用。
    • 确实很极端,但仍然很高兴知道
    • @voddan 请详细说明。加上问题是关于创建一个简单的单行而不是创建最有效的解决方案;-)
    【解决方案5】:

    除了miensolanswer 之外,还有多种方法可以将属性值复制到函数变量中以启用智能转换。例如:

    1. 中介功能:

      class Person(var name: String? = null, var age: Int? = null) {
          fun test() = test(name, age)
          private fun test(name: String?, age: Int?) {
              if (name != null && age != null)
                  doSth(name, age) //smart cast possible
          }
      
          fun doSth(someValue: String, someValue2: Int) {
      
          }
      }
      
    2. 匿名函数:

      class Person(var name: String? = null, var age: Int? = null) {
          fun test() = (fun(name: String?, age: Int?) {
              if (name != null && age != null)
                  doSth(name, age) //smart cast possible
          })(name, age)
      
          fun doSth(someValue: String, someValue2: Int) {
      
          }
      }
      
    3. 默认参数:

      class Person(var name: String? = null, var age: Int? = null) {
          fun test(name: String? = this.name, age: Int? = this.age) {
              if (name != null && age != null)
                  doSth(name, age) //smart cast possible
          }
      
          fun doSth(someValue: String, someValue2: Int) {
      
          }
      }
      

    【讨论】:

      【解决方案6】:

      可以定义一个内联方法,允许您采用 N 个参数以避免嵌套lets(我的答案基于this)。

      inline fun <T1: Any, T2: Any, R: Any> safeLet(p1: T1?, p2: T2?, block: (T1, T2)->R?): R? {
          return if (p1 != null && p2 != null) block(p1, p2) else null
      }
      

      然后

      fun test() {
          safeLet(name, age, {name, age -> 
              doSth(name, age) //smart cast
          });
      }
      

      【讨论】:

        【解决方案7】:

        我在将文本分配给具有相同问题描述的 textview 时遇到了问题。 我所做的只是在我的文本视图名称后加上双感叹号。 例如:

        var name:TextView?=null
        name = findViewById(R.id.id_name)
        name!!.text = "Your text"
        

        【讨论】:

          猜你喜欢
          • 2021-07-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-31
          • 2016-02-15
          • 1970-01-01
          • 2011-01-09
          • 1970-01-01
          相关资源
          最近更新 更多