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.Adam Powell
08/31/2020, 3:52 PM@Stable
type a
and b
. If you call
val param = if (something) a else b
MyComposable(param)
then this behavior becomes relevant.Adam Powell
08/31/2020, 3:54 PMMyComposable
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
.Adam Powell
08/31/2020, 3:55 PMa
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.Adam Powell
08/31/2020, 3:55 PMMyComposable
will run again with a
as the captured parameter, without recomposing the caller.Adam Powell
08/31/2020, 3:56 PMa == b
. The stable contract says one equal instance is as good as another and we can skip the call.Adam Powell
08/31/2020, 3:57 PMMyComposable(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.Adam Powell
08/31/2020, 3:58 PMa == b
their future mutation destinies must also be linked.Adam Powell
08/31/2020, 3:58 PM.equals
for a @Stable
mutable type.Adam Powell
08/31/2020, 4:00 PMAdam Powell
08/31/2020, 4:00 PMvar
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 AMfengdai
09/01/2020, 1:56 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
?fengdai
09/01/2020, 9:05 AMval 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 })
}
}
}
fengdai
09/01/2020, 9:48 AMColors
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