Nullability ใน Dart คืออะไร (ไม่ใช่โมฆะโดยค่าเริ่มต้น)?


27

ฉันเคยได้ยินเกี่ยวกับคุณลักษณะความปลอดภัยภาษาใหม่ของ Dart null ซึ่งขณะนี้เป็น " การทดสอบที่ไม่เป็นโมฆะ" มันควรจะแนะนำไม่ใช่ nullableโดยค่าเริ่มต้น

ข้อกำหนดคุณลักษณะที่สามารถพบได้ที่นี่และภาษาที่ปัญหา GitHub ที่นี่

มันทำงานอย่างไรและฉันจะลองได้ที่ไหน?

คำตอบ:


49

ไม่เป็นโมฆะ (โดยค่าเริ่มต้น)

ไม่ใช่ nullable (ค่าเริ่มต้น) การทดสอบปัจจุบันสามารถพบได้ที่nullsafety.dartpad.dev
โปรดทราบว่าคุณสามารถอ่านข้อมูลจำเพาะเต็มได้ที่นี่และแผนงานเต็มได้ที่นี่


ค่าเริ่มต้นที่ไม่เป็นโมฆะหมายถึงอะไร

void main() {
  String word;
  print(word); // illegal

  word = 'Hello, ';
  print(word); // legal
}

ดังที่คุณเห็นด้านบนตัวแปรที่ไม่เป็นโมฆะโดยค่าเริ่มต้นหมายความว่าตัวแปรทุกตัวที่ประกาศตามปกติไม่สามารถเป็นnullได้ ดังนั้นการดำเนินการใด ๆ ที่เข้าถึงตัวแปรก่อนที่จะได้รับมอบหมายนั้นผิดกฎหมาย
นอกจากnullนี้ไม่อนุญาตให้กำหนดตัวแปรที่ไม่ใช่ค่า nullable:

void main() {
  String word;

  word = null; // forbidden
  world = 'World!'; // allowed
}

สิ่งนี้จะช่วยฉันได้อย่างไร

หากตัวแปรไม่สามารถลบล้างได้คุณสามารถมั่นใจได้ว่ามันจะไม่มีวันnullเปลี่ยนแปลง ด้วยเหตุนี้คุณจึงไม่จำเป็นต้องตรวจสอบล่วงหน้า

int number = 4;

void main() {
  if (number == null) return; // redundant

  int sum = number + 2; // allowed because number is also non-nullable
}

จำ

ฟิลด์อินสแตนซ์ในคลาสต้องเริ่มต้นถ้าฟิลด์นั้นไม่สามารถ null ได้:

class Foo {
  String word; // forbidden

  String sentence = 'Hello, World!'; // allowed
}

ดูlateด้านล่างเพื่อปรับเปลี่ยนพฤติกรรมนี้

ประเภทที่มีค่าเป็นโมฆะ ( ?)

คุณสามารถใช้ประเภท nullableโดยผนวกเครื่องหมายคำถาม?กับประเภทตัวแปร:

class Foo {
  String word; // forbidden

  String? sentence; // allowed
}

nullableตัวแปรไม่ต้องมีการเริ่มต้นก่อนที่จะสามารถนำมาใช้ มันถูกเริ่มต้นnullตามค่าเริ่มต้น:

void main() {
  String? word;

  print(word); // prints null
}

!

ผนวก!กับตัวแปรใดeจะโยนข้อผิดพลาด runtimeถ้าeเป็นโมฆะและอื่น ๆ แปลงเป็นไม่ใช่ nullablevค่า

void main() {
  int? e = 5;
  int v = e!; // v is non-nullable; would throw an error if e were null

  String? word;
  print(word!); // throws runtime error if word is null

  print(null!); // throws runtime error
}

late

คำหลักlateสามารถใช้เพื่อทำเครื่องหมายตัวแปรที่จะเริ่มต้นในภายหลังเช่นไม่ใช่เมื่อมีการประกาศ แต่เมื่อมีการเข้าถึง นอกจากนี้ยังหมายความว่าเราสามารถมีฟิลด์อินสแตนซ์ที่ไม่เป็นโมฆะซึ่งจะเริ่มต้นได้ในภายหลัง:

class ExampleState extends State {
  late String word; // non-nullable

  @override
  void initState() {
    super.initState();

    // print(word) here would throw a runtime error
    word = 'Hello';
  }
}

การเข้าถึงwordก่อนที่จะเริ่มต้นจะทำให้เกิดข้อผิดพลาดรันไทม์

late final

ตัวแปรสุดท้ายสามารถทำเครื่องหมายได้ช้ากว่านี้:

late final int x = heavyComputation();

ที่นี่heavyComputationจะถูกเรียกเพียงครั้งเดียวเท่านั้นที่มีการxเข้าถึง นอกจากนี้คุณยังสามารถประกาศแบบlate finalไม่มี initializer ซึ่งเหมือนกับการมีเพียงlateตัวแปร แต่สามารถกำหนดได้เพียงครั้งเดียวเท่านั้น

late final int x;
// w/e
x = 5; // allowed
x = 6; // forbidden

โปรดทราบว่าขณะนี้ตัวแปรระดับบนสุดหรือแบบสแตติกที่มี initializer จะได้รับการประเมินlateไม่ว่าจะเป็นfinalก็ตาม

required

ก่อนหน้านี้มีคำอธิบายประกอบ ( @required) ตอนนี้ในตัวเป็นตัวดัดแปลง อนุญาตให้ทำเครื่องหมายพารามิเตอร์ที่ตั้งชื่อใด ๆ (สำหรับฟังก์ชั่นหรือคลาส) เป็นrequiredซึ่งทำให้พวกเขาไม่เป็นโมฆะ:

void allowed({required String word}) => null;

นี่หมายความว่าหากพารามิเตอร์ควรไม่เป็นโมฆะก็จำเป็นต้องทำเครื่องหมายเป็นrequiredหรือมีค่าเริ่มต้น:

void allowed({String word = 'World'}) => null;

void forbidden({int x}) // compile-time error because x can be null (unassigned)
    =>
    null;

พารามิเตอร์ที่มีชื่ออื่นใดจะต้องเป็นโมฆะ :

void baz({int? x}) => null;

?[]

ตัวดำเนินการ null ที่ทราบ?[]ถูกเพิ่มสำหรับตัวดำเนินการดัชนี[]:

void main() {
  List<int>? list = [1, 2, 3];

  int? x = list?[0]; // 1
}

ดูยังบทความนี้เกี่ยวกับการตัดสินใจไวยากรณ์

?..

ผู้ประกอบการน้ำตกในขณะนี้นอกจากนี้ยังมีผู้ประกอบการตระหนักถึง null ?..ใหม่:

มันทำให้เกิดการดำเนินงานที่น้ำตกดังต่อไปนี้เท่านั้นที่สามารถดำเนินการถ้าผู้รับเป็นไม่เป็นโมฆะ ดังนั้น, ?..จะต้องเป็นตัวดำเนินการเรียงซ้อนแรกในลำดับเรียงซ้อน:

void main() {
  Path? path;

  // Will not do anything if path is null.
  path
    ?..moveTo(3, 4)
    ..lineTo(4, 3);

  // This is a noop.
  (null as List)
    ?..add(4)
    ..add(2)
    ..add(0);
}

Never

เพื่อหลีกเลี่ยงความสับสน: นี่ไม่ใช่สิ่งที่นักพัฒนาต้องกังวล ฉันต้องการพูดถึงมันเพื่อความสมบูรณ์

Neverเป็นไปได้ชนิดเช่นที่มีอยู่ก่อนหน้านี้Null( ไม่null ) dart:coreที่กำหนดไว้ใน ทั้งสองคลาสนี้ไม่สามารถขยายนำไปใช้หรือผสมกันได้ดังนั้นจึงไม่ต้องการใช้

เป็นหลักNeverหมายความว่าไม่อนุญาตให้ใช้ประเภทและNeverไม่สามารถสร้างอินสแตนซ์ได้
แต่ไม่มีอะไรNeverใน List<Never>ความพึงพอใจข้อ จำกัด ประเภททั่วไปของรายการซึ่งหมายความว่ามันจะต้องมีที่ว่างเปล่า List<Null>อย่างไรก็ตามสามารถมีnull:

// Only valid state: []
final neverList = <Never>[
  // Any value but Never here will be an error.
  5, // error
  null, // error

  Never, // not a value (compile-time error)
];

// Can contain null: [null]
final nullList = <Null>[
  // Any value but Null will be an error.
  5, // error
  null, // allowed

  Never, // not a value (compile-time error)
  Null, // not a value (compile-time error)
];

ตัวอย่าง: เรียบเรียงจะอนุมานList<Never>สำหรับที่ว่างเปล่า ไม่ควรถูกใช้โดยโปรแกรมเมอร์เท่าที่ฉันกังวลconst List<T>
Never


5
คุณสามารถให้บางสถานการณ์ที่Neverสามารถใช้งานได้หรือไม่?
Ramses Aldama

2
เราได้ตัดสินใจที่จะใช้ "? []" สำหรับตัวดำเนินการดัชนีที่ไม่รู้จักแทน "?. []" หลังมีความซับซ้อนทางไวยากรณ์เล็กน้อย แต่เป็นสิ่งที่ผู้ใช้ต้องการ
มหานคร

@RamsesAldama ฉันได้เพิ่มบางสิ่ง สเปคที่ฉันเชื่อมโยงกับการกล่าวถึงมากขึ้น
creativecreatorormaybenot

@munificent ข้อมูลจำเพาะและการทดสอบยังคงล้าสมัยอยู่ ขอบคุณสำหรับการชี้ให้เห็น
creativecreatorormaybenot

1
ควรชี้ให้เห็นว่าlate finalในสมาชิกหรือตัวแปรอินสแตนซ์จะตรวจสอบเฉพาะที่รันไทม์ ไม่สามารถตรวจสอบเวลาการพัฒนาหรือเวลารวบรวมเนื่องจากปัญหาการหยุดทำงาน ดังนั้นคุณจะไม่ได้รับความช่วยเหลือจาก IDE
เกรแฮม
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.