【问题标题】:How do I make method param mutable in Kotlin?如何在 Kotlin 中使方法参数可变?
【发布时间】:2020-03-29 22:47:06
【问题描述】:

我正在 Android 项目中实现 SpinnerAdapter。所以我必须重写getView(i: Int, convertView: View, parent: ViewGroup) 方法。所以convertView在这里是为了重用现有视图并减少内存使用和GC发生。所以如果是null,我必须创建视图并使用已经创建的视图。

所以其实我得写这样的东西(google官方推荐):

if (view == null) {
    view = View.inflate(context, R.layout.item_spinner, parent)
    view.tag(Holder(view))
} else {
    (view.tag as Holder).title.text = getItem(i)
}

但是 Kotlin 不允许写入参数。 我在网上查到的是一个官方blog post,说从2013年2月开始就不可能了。

所以我想知道是否有任何解决方法?

【问题讨论】:

  • 使用单独的变量。无需为参数赋值。
  • than convertView 参数将始终为空,因此每次都会创建新视图。它使视图回收机制完全无用
  • @oleg.semen 不,如果您从getView 方法返回非空View,则View 稍后将用作convertView - 正如亨利所说,使用单独的变量
  • 您似乎误解了 Java 中的参数是如何工作的。它们基本上是用实际参数初始化的局部变量。在方法内更改它们对调用者没有影响。
  • @pskink 谢谢,明白了

标签: android kotlin


【解决方案1】:

这里有两个问题。

首先,您错误地假设在 Java 中修改 view 会在当前函数范围之外执行任何操作。它不是。您将该参数设置为新值不会影响本地函数范围之外的任何内容。

View getView(int i, View view, ViewGroup parent) {
   // modify view here does nothing to the original caller reference to view
   // but returning a view does do something
}

接下来,在 Kotlin 中,所有参数都是 final(JVM 修饰符,也与 Java 中的 final 修饰符相同)。这段代码的 Kotlin if 语句版本将是:

fun getView(i: Int, view: View?, parent: ViewGroup): View {
   return if (view == null) {
       val tempView = View.inflate(context, R.layout.item_spinner, parent)
       tempView.tag(Holder(tempView))
       tempView
   } else { 
       (view.tag as Holder).title.text = getItem(i)
       view
   }
}

或避免新的局部变量:

fun getView(i: Int, view: View?, parent: ViewGroup): View {
   return if (view == null) {
       View.inflate(context, R.layout.item_spinner, parent).apply {
           tag(Holder(this)) // this is now the new view
       }
   } else { 
       view.apply { (tag as Holder).title.text = getItem(i) }
   }
}

fun getView(i: Int, view: View?, parent: ViewGroup): View {
   if (view == null) {
       val tempView = View.inflate(context, R.layout.item_spinner, parent)
       tempView.tag(Holder(tempView))
       return tempView
   } 

   (view.tag as Holder).title.text = getItem(i)
   return view
}

或使用?.?: 空运算符与apply() 结合使用:

fun getView(i: Int, view: View?, parent: ViewGroup): View {
    return view?.apply { 
                (tag as Holder).title.text = getItem(i) 
           } ?: View.inflate(context, R.layout.item_spinner, parent).apply {
                     tag(Holder(this))
                }
}

还有另外 10 种变体,但您可以尝试看看自己喜欢什么。

使用相同的名称来隐藏变量被认为不是一个好的做法(但允许),这就是编译器警告的原因。以及为什么您会看到上面的变量名从 view 更改为 tempView

【讨论】:

    【解决方案2】:

    有一个肮脏但有用的方法来实现这一点。

    fun a(b: Int) {
       var b = b
       b++ // this compiles
    }
    

    【讨论】:

    • 似乎不起作用。我不知道这是做什么的,但我认为它只会关闭编译器并修改它然后丢弃的局部变量。
    • 如果在Java中重新赋值一个函数参数,是不是就扔掉了?如果你不知道这是做什么的,这似乎不起作用?
    • @Mitch 这确实有效。无论如何,您所说的“扔掉”是什么意思?局部变量 (var b) 将隐藏传递的参数 b。这意味着在此声明之后访问 b 的任何内容都将访问定义为 var b 的任何内容
    • 在 android studio 中添加 @Suppress("NAME_SHADOWING") 否则会惹恼你
    • 这将编译但不能按预期运行。如果你调用 a(myInt) myint 不会改变,因为我们在这里有一个按值而不是按引用的赋值。 b++ 增加函数内部的 b 而不是外部
    【解决方案3】:

    Kotlin支持可变参数。

    我想向您推荐这个discussion in kotlinlang.org

    【讨论】:

      【解决方案4】:

      正式来说,你不能重写方法参数。您能做的最好的事情就是“隐藏”参数变量。

      所以你可以这样做(虽然不知道为什么你想要阴影但你可以)

      getView(i: Int, view: View?, parent: ViewGroup) {
        val view = view ?: View.inflate(context, R.layout.item_spinner, parent)
                               .apply { tag(Holder(view)) }
        (view.tag as Holder).title.text = getItem(i)
      }
      

      【讨论】:

        猜你喜欢
        • 2023-02-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-12-22
        • 1970-01-01
        • 2014-07-27
        • 1970-01-01
        相关资源
        最近更新 更多