如何在Kotlin类中使用解构声明?

huangapple go评论69阅读模式
英文:

How can I use destructuring declarations in a class in Kotlin?

问题

Code A

目前我在我的项目中使用 Code A我认为它太复杂了

但是 Code B 出现以下错误

仅允许对局部变量/值进行解构声明

我该如何优化 Code A

**Code A**

```kotlin
class PreferenceParameterState private constructor(context: Context) {
    var openEditDialog by mutableStateOf(false)
        private set

    fun set_OpenEditDialog(isShow: Boolean) {
        openEditDialog = isShow
    }
}

Code B

class PreferenceParameterState private constructor(context: Context) {
    val (openEditDialog, set_OpenEditDialog) = mutableStateOf(false)
}

Added Content

对于 Tenfour04:谢谢!

Code C 来自官方 项目

在 Code C 中,公共的 fun openDrawer()fun resetOpenDrawerAction() 可以最终改变 val drawerShouldBeOpened

按照您的意见,Code D 可能是一个不错的选择,但实际上,我很少看到 Code D,而 Code C 经常出现。

如果我要优化 Code A,我应该使用 Code E 吗?

Code C

class MainViewModel : ViewModel() {

    private val _drawerShouldBeOpened = MutableStateFlow(false)
    val drawerShouldBeOpened = _drawerShouldBeOpened.asStateFlow()

    fun openDrawer() {
        _drawerShouldBeOpened.value = true
    }

    fun resetOpenDrawerAction() {
        _drawerShouldBeOpened.value = false
    }
}

Code D

class MainViewModel : ViewModel() {
    var drawerShouldBeOpened = MutableStateFlow(false)
}

Code E

class PreferenceParameterState private constructor(context: Context) {
    var openEditDialog by mutableStateOf(false)
}

Added Content Again:

对于 Tenfour04:谢谢!

Code F 来自另一个 项目

而 Code F 与 Flow 不兼容,按照您的意见,Code F 应该优化为 Code G,这正确吗?

Code F

private val _freeTimeResponse = mutableStateListOf<Int>()
val freeTimeResponse: List<Int>
    get() = _freeTimeResponse

private val _superheroResponse = mutableStateOf<Superhero?>(null)
val superheroResponse: Superhero?
    get() = _superheroResponse.value

private val _takeawayResponse = mutableStateOf<Long?>(null)
val takeawayResponse: Long?
    get() = _takeawayResponse.value

private val _feelingAboutSelfiesResponse = mutableStateOf<Float?>(null)
val feelingAboutSelfiesResponse: Float?
    get() = _feelingAboutSelfiesResponse.value

private val _selfieUri = mutableStateOf<Uri?>(null)
val selfieUri
    get() = _selfieUri.value

fun onSuperheroResponse(superhero: Superhero) {
    _superheroResponse.value = superhero
    _isNextEnabled.value = getIsNextEnabled()
}

Code G

var freeTimeResponse by mutableStateListOf<Int>()

var superheroResponse by mutableStateOf<Superhero?>(null)

var takeawayResponse by mutableStateOf<Long?>(null)

...

<details>
<summary>英文:</summary>

At present, I use Code A in my project, I think it&#39;s too complex.

But Code B get the following error

*Destructuring declarations are only allowed for local variables/values*

How can I optimize Code A ?

**Code A**

    class PreferenceParameterState private constructor(context: Context) {
        var openEditDialog by mutableStateOf(false)
            private set
    
        fun set_OpenEditDialog(isShow: Boolean) {
            openEditDialog = isShow
        }
    }



**Code B**

    class PreferenceParameterState private constructor(context: Context) {
        val (openEditDialog, set_OpenEditDialog) = mutableStateOf(false)  
    }

**Added Content**

To Tenfour04: Thanks!

The Code C comes from the official [project][1].

In Code C,  the public `function  fun openDrawer()`  and  `fun resetOpenDrawerAction()`  can change `val drawerShouldBeOpened` finally.

By your opinion,  Code D would be good way, but in fact , I seldom see Code D, and Code C is often. 

If I  optimize Code A,  shoud I use Code E ?


**Code C**

    class MainViewModel : ViewModel() {
    
        private val _drawerShouldBeOpened = MutableStateFlow(false)
        val drawerShouldBeOpened = _drawerShouldBeOpened.asStateFlow()
    
        fun openDrawer() {
            _drawerShouldBeOpened.value = true
        }
    
        fun resetOpenDrawerAction() {
            _drawerShouldBeOpened.value = false
        }
    }


**Code D**

    class MainViewModel : ViewModel() {
        var drawerShouldBeOpened = MutableStateFlow(false)
    }


**Code E**

    class PreferenceParameterState private constructor(context: Context) {
        var openEditDialog by mutableStateOf(false) 
    }


**Added Content Again:**

To Tenfour04: Thanks!

The Code F  comes from another [project][2]

And Code F is not working with a `Flow`,  by your opinion, Code F should be optimized as Code G, is it correct?


**Code F**

        private val _freeTimeResponse = mutableStateListOf&lt;Int&gt;()
        val freeTimeResponse: List&lt;Int&gt;
            get() = _freeTimeResponse
    
        private val _superheroResponse = mutableStateOf&lt;Superhero?&gt;(null)
        val superheroResponse: Superhero?
            get() = _superheroResponse.value
    
        private val _takeawayResponse = mutableStateOf&lt;Long?&gt;(null)
        val takeawayResponse: Long?
            get() = _takeawayResponse.value
    
        private val _feelingAboutSelfiesResponse = mutableStateOf&lt;Float?&gt;(null)
        val feelingAboutSelfiesResponse: Float?
            get() = _feelingAboutSelfiesResponse.value
    
        private val _selfieUri = mutableStateOf&lt;Uri?&gt;(null)
        val selfieUri
            get() = _selfieUri.value
    
    
      fun onSuperheroResponse(superhero: Superhero) {
            _superheroResponse.value = superhero
            _isNextEnabled.value = getIsNextEnabled()
        }


**Code G**

    var freeTimeResponse by mutableStateListOf&lt;Int&gt;()
    
    var superheroResponse by mutableStateOf&lt;Superhero?&gt;(null)     
    
    var takeawayResponse by mutableStateOf&lt;Long?&gt;(null)
    
    ...

  [1]: https://github.com/android/compose-samples/blob/main/Jetchat/app/src/main/java/com/example/compose/jetchat/MainViewModel.kt
  [2]: https://github.com/android/compose-samples/blob/main/Jetsurvey/app/src/main/java/com/example/compose/jetsurvey/survey/SurveyViewModel.kt



</details>


# 答案1
**得分**: 1

让我开始分享一下解构的工作原理。

当类具有与您想要解构的组件数量相匹配的 `fun componentN()` 时,可以对数据进行解构。

**数据类解构**

与 `toString()`、`equals()` 和 `hashCode()` 一样,`data class` 在 Kotlin 中默认支持此功能。

例如,

```kotlin
data class DataClassDestructuring(
    val a: Int,
    val b: Int,
)

可以这样解构:

fun test() {
    val (dataClassA, dataClassB) = DataClassDestructuring(1, 2)
}

自定义类解构

我们还可以使用 operator fun componentN() 方法对其他类进行解构。

class CustomDestructureClass(
    private val x: Int = 0,
    private val y: Int = 0,
) {
    operator fun component1(): Int {
        return x
    }

    operator fun component2(): Int {
        return y
    }
}

这可以这样解构:

fun test() {
    val (customClassX, customClassY) = CustomDestructureClass(1, 2)
}

MutableState 解构

由于 MutableState 定义如下,它也支持解构。

interface MutableState<T> : State<T> {
    override var value: T
    operator fun component1(): T
    operator fun component2(): (T) -> Unit
}

fun test() {
    val (state, setState) = mutableStateOf("")
}

MutableStateFlow 解构

但是,MutableStateFlow 不能被解构。如果您查看其代码,您将找不到所需的组件操作符方法。



既然我们已经了解了解构是什么以及它是如何工作的,让我们看看在进行解构时会发生什么。

Kotlin文档中可以看到,

此语法称为解构声明。解构声明可以一次创建多个变量。

这说明解构可以一次创建多个变量。那么,为什么在函数中进行解构可以工作,但在类中却会出错呢?

fun test() {
    val (dataClassA, dataClassB) = DataClassDestructuring(1, 2) // 可行
}

class DestructuringDemo {
    val (dataClassA, dataClassB) = DataClassDestructuring(1, 2) // 错误
}

我们可以从错误消息中理解这一点,

解构声明仅允许用于局部变量/值。

查看Kotlin属性文档,我们可以观察到类定义了属性而不是直接的变量(或在这种上下文中的字段)。

由于属性支持 getter 和 setter,这在解构中不起作用。



问题的最后一个方面是如何在类中像ViewModel一样使用 MutableState

对于这个问题没有正确的答案,这取决于您的需求。

代码A - 公共getter和私有setter

var openEditDialog by mutableStateOf(false)
    private set

代码E - 公共getter和公共setter

var openEditDialog by mutableStateOf(false)

修改代码E,将 val 更改为 var - 公共getter和不可变(因此没有setter)

val openEditDialog by mutableStateOf(false)

代码F - 使用Kotlin Backing property

private val _superheroResponse = mutableStateOf<Superhero?>(null)
val superheroResponse: Superhero?
    get() = _superheroResponse.value

其他选项 - 您可以像这样在属性上声明getter和setter。



如何选择?

在添加更多信息之前,我也想补充一点,Google Devs已经提到在ViewModel中添加可变状态是可以的,但是关于是否这是一个好方法存在不同的意见,因为它来自于Compose Runtime包。

我个人更喜欢在ViewModel中使用Flow而不是Compose State。这完全是个人意见。

如果您决定在ViewModel中使用Compose状态,这将是一个不错的方法。

private val _superheroResponse = mutableStateOf<Int?>(null)
val superheroResponse: State<Int?>
    get() = _superheroResponse

公共的 superheroResponse 应该明确指定类型为 State 而不是 MutableState。然后,您可以具有自定义的getter和setter,在检索或更新值之前或之后执行任何验证或业务操作。

问题中的代码E使用.value 返回值,但我们在这里返回了状态,因此可以在可组合中观察到这一点。

英文:

Let me start by sharing how destructuring works.

You can destructure data when the class has fun componentN() for the number of components you want to destructure it to.

Data class destructuring

A data class has this support out of the box by Kotlin similar to toString(), equals(), and hashCode().

Example,

data class DataClassDestructuring(
    val a: Int,
    val b: Int,
)

Can be destructured like this,

fun test() {
    val (dataClassA, dataClassB) = DataClassDestructuring(1, 2)
}

Custom class destructuring

We can also have destructuring for other classes using operator fun componentN() methods.

class CustomDestructureClass(
    private val x: Int = 0,
    private val y: Int = 0,
) {
    operator fun component1(): Int {
        return x
    }

    operator fun component2(): Int {
        return y
    }
}

This can be destructured like this,

fun test() {
    val (customClassX, customClassY) = CustomDestructureClass(1, 2)
}

MutableState destructuring

As MutableState is defined like this, it also supports destructuring.

interface MutableState&lt;T&gt; : State&lt;T&gt; {
    override var value: T
    operator fun component1(): T
    operator fun component2(): (T) -&gt; Unit
}

fun test() {
    val (state, setState) = mutableStateOf(&quot;&quot;)
}

MutableStateFlow destructuring

But, MutableStateFlow can not be destructured. If you look into its code you will not find the required component operator methods.



Now that we went through what destructuring is and how it works. Let's see what happens when we do it.

From the Kotlin Docs,

> This syntax is called a destructuring declaration. A destructuring declaration creates multiple variables at once.

This states that destructuring creates multiple variables at once.
So, why does it work when destructuring in a function like, but the same gives an error in a class?

fun test() {
    val (dataClassA, dataClassB) = DataClassDestructuring(1, 2) // Works
}

class DestructuringDemo {
    val (dataClassA, dataClassB) = DataClassDestructuring(1, 2) // Error
}

We can understand this from the error message,
> Destructuring declarations are only allowed for local variables/values.

Looking into Kotlin Docs for Properties, we can observe that the class defines properties and not variables (or fields in this context) directly.

As properties support getter and setter, this does not work for destructuring.



The last aspect of the question was how to use MutableState in a class like ViewModel.

There is no correct answer for this, it depends on what you want.

Code A - Public getter and private setter

var openEditDialog by mutableStateOf(false)
    private set

Code E - Public getter and public setter

var openEditDialog by mutableStateOf(false) 

Modified Code E changing val to var - Public getter and immutable (so no setter)

val openEditDialog by mutableStateOf(false) 

Code F - Using Kotlin Backing property

private val _superheroResponse = mutableStateOf&lt;Superhero?&gt;(null)
val superheroResponse: Superhero?
    get() = _superheroResponse.value

Other options - You can have getters and setter declared along with the property like this



How to choose?

Before adding further info, I would like to add this as well Google Devs have mentioned that is fine to add mutable state in ViewModel, but there is mixed opinion on whether it is a good approach as it is from Compose Runtime package.

I personally prefer using Flow in ViewModel over Compose State. This is completely a personal opinion.

If you are proceeding with having Compose state in ViewModel, this would be a good approach.

private val _superheroResponse = mutableStateOf&lt;Int?&gt;(null)
val superheroResponse: State&lt;Int?&gt;
    get() = _superheroResponse

The public superheroResponse should have the type mentioned explicitly as State and not MutableState. Then you can have a custom getter and setter to perform any validation or business operations before or after retrieving or updating the value.

The code E in the question is returning the value using .value, but we are returning the state here and hence this can be observed in the Composable.

huangapple
  • 本文由 发表于 2023年6月22日 11:33:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/76528454.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定