Null Safety
non-nullable โดย default
หัวข้อที่มีชื่อว่า “non-nullable โดย default”Dart มี sound null safety: type ไม่สามารถเก็บ null ได้เว้นแต่คุณจะบอกชัด ๆ ด้วย ? นี่คือการันตีแบบแข็ง ไม่ใช่ lint — ถ้า type system บอกว่าค่านั้น non-null จะเป็น null ตอน runtime ไม่ได้จริง ๆ
int a = 1; // non-nullable — เป็น null ไม่ได้เลยint? b = null; // nullable — ? opt-in ให้เป็น null ได้
a = null; // ❌ compile error: a เป็น null ไม่ได้b = 42; // ✅ nullable ก็เก็บค่าจริงได้เหมือนกันคำสำคัญคือ “sound”: เพราะการันตีนี้แน่นหนา compiler จึง optimize ตามได้ และคุณก็ เชื่อ type ได้จริง widget property แบบ String name จะไม่มาเซอร์ไพรส์คุณด้วย null
operator สี่ตัวที่คุณจะใช้ทุกวัน
หัวข้อที่มีชื่อว่า “operator สี่ตัวที่คุณจะใช้ทุกวัน”String? maybeName;
// ?. — null-aware access: เรียกก็ต่อเมื่อไม่ null ไม่งั้นทั้งก้อนเป็น nullfinal len = maybeName?.length; // int? — เป็น null ถ้า maybeName เป็น null
// ?? — if-null: ให้ค่า fallback เมื่อฝั่งซ้ายเป็น nullfinal safe = maybeName ?? 'Guest'; // String — ไม่มีทางเป็น null
// ??= — assign ก็ต่อเมื่อตอนนี้เป็น nullmaybeName ??= 'Default';
// ! — null assertion: "ฉันสัญญาว่าอันนี้ไม่ null" (throw ถ้าคุณคิดผิด)final definite = maybeName!; // String — อันตรายถ้ามันเป็น null จริงoperator ! คือตัวที่ต้องเคารพ: ! สั่งปิดปาก compiler ด้วยการ assert ว่า non-null และถ้าคุณผิด จะ throw ตอน runtime ทุก ! คือการเดิมพันเล็ก ๆ — ใช้เมื่อคุณพิสูจน์ได้ว่าค่านั้น non-null เท่านั้น
flow analysis narrow ให้คุณ
หัวข้อที่มีชื่อว่า “flow analysis narrow ให้คุณ”คุณแทบไม่ต้องใช้ ! เพราะ flow analysis ของ Dart จะ narrow nullable เป็น non-null หลังผ่านการเช็ค:
void greet(String? name) { if (name == null) return; // จากตรงนี้ลงไป Dart รู้ว่า name เป็น String (ไม่ใช่ String?) — ไม่ต้องใช้ ! print(name.toUpperCase());}flowchart TB
start["name: String?"] --> check{"name == null?"}
check -->|yes| ret["return early"]
check -->|no| promoted["name: String
(promote แล้ว ใช้ได้ปลอดภัย)"] มีจุดที่กัด Flutter dev อยู่บ่อย: flow analysis ทำงานกับ local variable ไม่ใช่กับ instance field หรือ getter (พวกนั้นอาจเปลี่ยนค่าระหว่างเช็คกับใช้งาน) สำหรับ field ที่เป็น nullable ให้ copy ไปเป็น local ก่อน แล้วค่อยเช็ค local นั้น
late: สัญญาว่าจะ initialize ก่อนใช้
หัวข้อที่มีชื่อว่า “late: สัญญาว่าจะ initialize ก่อนใช้”late บอกว่า “อันนี้จะเป็น non-null ตอนที่มีใครอ่าน แค่ตอนประกาศยังไม่มีค่า” มีประโยชน์กับค่าที่ set ใน initState หรือคำนวณครั้งเดียว
late final String config; // ยังไม่มีค่า แต่สัญญาว่าจะมีก่อนอ่านครั้งแรก// ถ้าอ่านก่อน assign จะได้ LateInitializationErrorใช้ late อย่างประหยัด — เพราะเป็นการแลกการันตีตอน compile-time ไปเป็นตอน runtime late ที่ใช้ผิดก็คือ ! ในคราบปลอมนั่นเอง