fengdai
08/31/2020, 3:02 AM@Stable
’s guarantees? > The result of equals will always return the same result for the same two instances.Zach Klippenstein (he/him) [MOD]
08/31/2020, 2:37 PM===
and ==
must effectively return the same results for any two @Stable
objects.Adam Powell
08/31/2020, 3:50 PM@Stable
, then compose can declare that .equals
indicates one instance is as good as another and that they are equal in how they will change and notify compose of any future changes.@Stable
type a
and b
. If you call
val param = if (something) a else b
MyComposable(param)
then this behavior becomes relevant.MyComposable
is initially called with a
, and the caller recomposes and it is called with a
again, we compare a == a
, and determine we can skip the call to MyComposable
.a
internally changes, (e.g. a field backed by mutableStateOf
changes) the bits inside MyComposable
that read those state values will run again - it's an internal lambda capture of the parameters from the last time MyComposable
ran without skipping.MyComposable
will run again with a
as the captured parameter, without recomposing the caller.a == b
. The stable contract says one equal instance is as good as another and we can skip the call.MyComposable(b)
, the internal restart capture still points to a
. If b
changes and a
doesn't reflect those same changes over time, MyComposable
won't recompose.a == b
their future mutation destinies must also be linked..equals
for a @Stable
mutable type.var
data class constructor properties is a bad idea - it makes that object really dangerous to use as a key in hash tables.fengdai
09/01/2020, 1:28 AM@Stable
. And I think a
does not equal b
in this case:
val initialValue = ""
val a = mutableStateOf(initialValue)
val b = mutableStateOf(initialValue)
Even they hold the same initial value but they can be mutated respectively later, which can make them not equal anymore. (Am I right?) So in which case can two instances of MutableState be equal?Adam Powell
09/01/2020, 2:01 AMSnapshotMutableState
does not implement `.equals`: https://cs.android.com/androidx/platform/frameworks/support/+/androidx-master-dev:compose/runtime/runtime/src/commonMain/kotlin/androidx/compose/runtime/MutableState.kt;l=288fengdai
09/01/2020, 7:21 AMandroidx.compose.material.Colors
is annotated with @Stable
. And it's mutable, also implements .equals
. As you mentioned above, if two instances of Colors a == b
then a
will be treated as the same as b
. But how can it comply with the contract when a
is changed? Is there some underlying mechanism of Compose that will build a link between a
and b
?val darkColors = darkColors()
val lightColors = lightColors()
@Composable
fun UnstableColors() {
val a = lightColors
var b: Colors? by remember { mutableStateOf(null) }
var darkTheme by remember { mutableStateOf(false) }
Column {
MaterialTheme(
colors = if (darkTheme) darkColors else lightColors,
) {
if (b == null) {
b = MaterialTheme.colors
}
Surface {
Column {
Text(text = if (darkTheme) "darkTheme" else "lightTheme")
Text(text = "a hash: ${a.identityHashCode()}")
Text(text = "b hash: ${b.identityHashCode()}")
Text(text = "a ${if (a == b) "==" else "!="} b")
}
}
}
Surface {
Switch(checked = darkTheme, onCheckedChange = { darkTheme = it })
}
}
}
Colors
is designed this way for performance consideration. But will the break of contract cause unexpected behavior when it’s misused by us?Adam Powell
09/01/2020, 2:42 PMColors
- thank you 🙂 I'll follow up with the team