如何将对不同变量/类属性的引用作为方法参数而不是值?

Posted

技术标签:

【中文标题】如何将对不同变量/类属性的引用作为方法参数而不是值?【英文标题】:How to put a reference to different variables/class properties as method parameter instead of a value? 【发布时间】:2022-01-24 03:11:03 【问题描述】:

我需要使用逻辑创建多个editTexts 来处理输入格式。不同的editTexts 将绑定到不同的变量,并保存到viewModel.home 的不同属性中。但是,editTexts 将使用相同的逻辑(尽管字符串、整数、双精度等不同),因此我想避免复制粘贴代码。

对于每个editText,我必须设置一个addTextChangedListener 并覆盖afterTextChanged 方法(见下文)。

我想通过从afterTextChanged 的每个覆盖中调用另一个方法 (customAfterTextChanged()) 来尽可能概括:

viewBinding.editTextAdress.addTextChangedListener(object: TextWatcher 
    override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) 
    override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) 
    override fun afterTextChanged(s: Editable?) 
        customAfterTextChanged()
)

代码中的其他一些地方我会定义方法:

        fun customAfterTextChanged () 
            if (viewBinding.adressEditText.text.toString() != "") 
                viewModel.home.adress = viewBinding.adressEditText.text.toString()
            
        

在上面的代码中,homeviewModel 是自定义类的实例,其中home 作为viewModel 中的实例存在。

我现在的问题是:我希望能够将viewBinding.cityEditText 而不是viewBinding.adressEditText 传递到方法中。或viewModel.home.city 而不是viewModel.home.adress。为了实现这一点,我认为需要将viewBinding.editTextAdressviewModel.home.adress 替换为特定于方法的变量,并以某种方式将viewModel.home.city 的引用传递给变量。

但是,我不知道该怎么做。有人可以编辑我的代码以使其在此特定情况下工作吗?我想如果我能得到一个可行的例子,我就可以翻译成所有其他的逻辑。

【问题讨论】:

您可能会发现一些扩展函数here 有助于简化 Kotlin 中的 TextWatcher 复制 谢谢。但是,我认为该链接主要解释了我已经理解的部分。从我所见,它并不能帮助我理解如何将类实例属性的引用作为参数传递给方法或 lambda,而只是如何将值作为参数。 【参考方案1】:

有不同的方法来完成你正在寻找的东西,但你会受到这样一个事实的限制,即你不能从你的 ViewModel 中“通过引用”传递字符串之类的东西,并像在 c++ 中那样修改它。 (Relevant discussion)。您必须在 Kotlin/Java 中使用稍微不同的模式。

选项 1 - 使用回调

fun customAfterTextChanged(et: EditText, onResult: (String)->Unit) 
    val s = et.text.toString()
    if (s.isNotEmpty()) 
        onResult(s)
    

那么你可以这样称呼它

customAfterTextChanged(viewBinding.adressEditText)  s ->
    viewModel.home.adress = s


customAfterTextChanged(viewBinding.cityEditText)  s ->
    viewModel.home.city = s

选项 2 - 移至 ViewModel(更好)

通常最好尝试将所有逻辑移至 ViewModel 以使其更易于测试。如果你这样做,如果使用 ktx 扩展,你的视图层最终会看起来像这样(没有逻辑 - 它只是在文本更改时调用 ViewModel)

viewBinding.adressEditText.doAfterTextChanged  e ->
    e?.let  viewModel.changedAddress(it.toString()) 

在 ViewModel 中你会拥有

fun changedAddress(a: String) 
    if( a.isNotEmpty() ) home.adress = a

这样做的好处是更容易对应用的行为进行单元测试。例如,要测试用户删除文本是否具有预期效果,您只需要这样的东西(而不是完整的 UI 测试)

model.changedAddress("123")
assertEqual("123", model.home.address)
model.changedAddress("")
assertEqual("123", model.home.address)

如果您的检查器逻辑更复杂,您始终可以像使用选项 1 一样在 ViewModel 中组合该逻辑 - 但它不需要与实际视图有任何联系

【讨论】:

谢谢!您的选项 1 对我理解语法和模式很有帮助。虽然由于逻辑比示例中更复杂,它并没有完全解决我的问题,但我认为我已经尽可能地做到了。

以上是关于如何将对不同变量/类属性的引用作为方法参数而不是值?的主要内容,如果未能解决你的问题,请参考以下文章

方法参数可以包含对其他变量的引用而不是包含值吗?

如何在 Python 中使用类变量作为默认参数值

引用类型用法总结

仅将 ArrayList 作为值而不是引用传递

JAVA基础-多态

python中的self