ทำไมใน Jetpack Compose ถึงไม่แนะนำให้ใช้ MutableState กับ Int, Float, Long, และ Double โดยตรง

ในการใช้งาน Jetpack Compose จะมีเรื่องของ State เข้ามาส่วนสำคัญในการทำงานเพื่อให้ Composable Function ทำงานหรือแสดงผลตาม State ที่ส่งเข้ามา จึงทำให้โค้ดบางส่วนของเรามีการใช้งาน MutableState หรือคำสั่งอย่าง mutableStateOf

โดย MutableState เป็น Interface ที่มี Value เป็น Generic ที่กำหนดเป็น Any? เพื่อให้ใช้กับข้อมูลอะไรก็ได้ตามที่นักพัฒนาต้องการ จึงทำให้นักพัฒนาสร้าง MutableState โดยกำหนด Value เป็นคลาสใดก็ได้ ยกตัวอย่างเช่น

data class Item( /* ... */ )

var item by remember { mutableStateOf<Item>(null) }
var name by remember { mutableStateOf<String>("John Doe") }

แต่เมื่อนักพัฒนาใช้ MutableState กับ Int, Float, Long, หรือ Double บน Android Studio ก็จะขึ้น Warning ขึ้นมาแบบนี้แทน

เพราะใน Jetpack Compose ได้เตรียมคำสั่งสำหรับข้อมูลที่เป็นตัวเลขไว้ให้ใช้แทน

Original Recommended
mutableStateOf<Int> mutableStateIntOf
mutableStateOf<Float> mutableStateFloatOf
mutableStateOf<Long> mutableStateLongOf
mutableStateOf<Double> mutableStateDoubleOf

ทำไมถึงไม่ให้ใช้ MutableState กับข้อมูลที่เป็นตัวเลขโดยตรงล่ะ?

เหตุผลหลักก็คือเรื่อง Performance และ Memory Consumption ที่เกิดขึ้นจากขั้นตอนที่เรียกว่า Boxing และ Unboxing สำหรับข้อมูลที่เป็น Primitive Data Type ใน Java เนื่องจากบน Kotlin จะมี Wrapper Class อย่าง Int, Float, Long, หรือ Double เพื่อทำให้ข้อมูลเหล่านี้ถูกมองเป็น Class ทั้งหมด

ดังนั้นเพื่อลด Overhead ในขั้นตอนดังกล่าวจึงทำให้ทีม Jetpack Compose เพิ่มคำสั่งสำหรับ Int, Float, Long, และ Double ให้โดยเฉพาะเพื่อข้ามขั้นตอน Boxing และ Unboxing ออกไป

โดยเบื้องหลังของคำสั่งเหล่านี้ก็จะเป็น MutableState ที่สร้างขึ้นมาให้กับข้อมูลตัวเลขแต่ละประเภทโดยเฉพาะ ไม่ว่าจะเป็น MutableIntState, MutableFloatState, MutableLongState, และ MutableDoubleState

ยกตัวอย่างเช่นคำสั่งของ MutableIntState ที่จะมีหน้าตาเป็นแบบนี้

@Stable
@JvmDefaultWithCompatibility
interface MutableIntState : IntState, MutableState<Int> {
    @get:AutoboxingStateValueProperty("intValue")
    @set:AutoboxingStateValueProperty("intValue")
    override var value: Int
        @Suppress("AutoBoxing") get() = intValue
        set(value) {
            intValue = value
        }

    override var intValue: Int
}

สรุป

ด้วยเหตุผลทางด้าน Performance และ Memory Consumption ที่เกิดขึ้นจากการ Boxing และ Unboxing สำหรับตัวแปรที่เป็น Primitive Data Type บน Java จึงทำให้ทีม Jetpack Compose เพิ่มคำสั่งสำหรับข้อมูลที่เป็นตัวเลขในแต่ละประเภทให้โดยเฉพาะ แทนการใช้ MutableState โดยตรง

ดังนั้นถ้านักพัฒนาต้องการสร้าง State สำหรับตัวเลขและค่าไม่มีทางเป็น null ก็ควรใช้คำสั่ง mutable<Type>StateOf แทน mutableStateOf ตามที่ Jetpack Compose แนะนำดีกว่า เพราะจะได้ Performance ที่ดีขึ้นในขณะที่ยังได้ผลลัพธ์ที่เหมือนเดิมทั้งหมด