Nullability in Dart (Không thể null theo mặc định) là gì?


27

Tôi đã nghe nói về tính năng ngôn ngữ an toàn null null mới, hiện tại là " thử nghiệm " không thể vô hiệu ". Nó được cho là giới thiệu không nullable theo mặc định .

Đặc tả tính năng có thể được tìm thấy ở đâyvấn đề GitHub ngôn ngữ ở đây .

Làm thế nào nó hoạt động và tôi có thể thử nó ở đâu?

Câu trả lời:


49

Không nullable (theo mặc định)

Thử nghiệm không nullable (theo mặc định) hiện có thể được tìm thấy tại nullsquil.dartpad.dev .
Hãy nhớ rằng bạn có thể đọc thông số kỹ thuật đầy đủ ở đâylộ trình đầy đủ ở đây .


Không mặc định có nghĩa là gì?

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

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

Như bạn có thể thấy ở trên, một biến không thể rỗng theo mặc định có nghĩa là mọi biến được khai báo bình thường đều không thể null. Do đó, bất kỳ hoạt động truy cập vào biến trước khi nó được chỉ định là bất hợp pháp.
Ngoài ra, việc gán nullcho một biến không có giá trị cũng không được phép:

void main() {
  String word;

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

Điều này giúp tôi như thế nào?

Nếu một biến là không thể rỗng , bạn có thể chắc chắn rằng nó không bao giờ null. Do đó, bạn không bao giờ cần phải kiểm tra trước.

int number = 4;

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

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

Nhớ lại

Các trường sơ thẩm trong các lớp phải được khởi tạo nếu chúng không thể rỗng:

class Foo {
  String word; // forbidden

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

Xem latebên dưới để sửa đổi hành vi này.

Các loại không thể ( ?)

Bạn có thể sử dụng các loại nullable bằng cách nối thêm một dấu hỏi ?vào một loại biến:

class Foo {
  String word; // forbidden

  String? sentence; // allowed
}

Một biến nullable không cần phải được khởi tạo trước khi nó có thể được sử dụng. Nó được khởi tạo như nullmặc định:

void main() {
  String? word;

  print(word); // prints null
}

!

Áp dụng !cho bất kỳ biến nào esẽ gây ra lỗi thời gian chạy nếu elà null và nếu không thì chuyển đổi nó thành một giá trị không thể rỗngv .

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

Từ khóa latecó thể được sử dụng để đánh dấu các biến sẽ được khởi tạo sau , tức là không phải khi chúng được khai báo mà là khi chúng được truy cập. Điều này cũng có nghĩa là chúng ta có thể có các trường đối tượng không thể rỗng được khởi tạo sau:

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

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

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

Truy cập wordtrước khi nó được khởi tạo sẽ gây ra lỗi thời gian chạy.

late final

Biến cuối cùng bây giờ cũng có thể được đánh dấu muộn:

late final int x = heavyComputation();

Ở đây heavyComputationsẽ chỉ được gọi một lần xđược truy cập. Ngoài ra, bạn cũng có thể khai báo late finalkhông có bộ khởi tạo, giống như chỉ có một latebiến, nhưng nó chỉ có thể được gán một lần.

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

Lưu ý rằng tất cả các biến cấp cao nhất hoặc tĩnh với trình khởi tạo bây giờ sẽ được đánh giá late, bất kể chúng là gì final.

required

Trước đây là một chú thích ( @required), bây giờ được tích hợp làm công cụ sửa đổi. Nó cho phép đánh dấu bất kỳ tham số được đặt tên nào (cho các hàm hoặc các lớp) là required, làm cho chúng không thể rỗng:

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

Điều này cũng có nghĩa là nếu một tham số không thể rỗng , thì nó cần được đánh dấu là requiredhoặc có giá trị mặc định:

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

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

Bất kỳ tham số được đặt tên nào khác phải là nullable :

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

?[]

?[]Toán tử nhận biết null đã được thêm cho toán tử chỉ mục []:

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

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

Xem thêm bài viết này về quyết định cú pháp .

?..

Toán tử ghép tầng bây giờ cũng có toán tử nhận biết null mới : ?...

Nó làm cho các hoạt động theo tầng sau chỉ được thực hiện nếu người nhận không phảinull . Do đó, ?..phải là toán tử phân tầng đầu tiên trong chuỗi phân tầng:

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

Để tránh nhầm lẫn: đây không phải là điều mà các nhà phát triển phải lo lắng. Tôi muốn đề cập đến nó vì lợi ích của sự hoàn chỉnh.

Neversẽ là một loại giống như trước đây Null( khôngnull ) được xác định trong dart:core. Cả hai lớp này không thể được mở rộng, thực hiện hoặc trộn lẫn, vì vậy chúng không có ý định sử dụng.

Về cơ bản, Nevercó nghĩa là không có loại nào được cho phép và Neverbản thân nó không thể được khởi tạo.
Không có gì nhưng Nevertrong một List<Never>thỏa mãn loại ràng buộc chung của danh sách, có nghĩa là nó phải trống . List<Null>tuy nhiên, có thể chứa 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)
];

Ví dụ: trình biên dịch sẽ suy ra List<Never>một khoảng trống const List<T> .
Neverkhông được sử dụng bởi các lập trình viên theo như tôi nghĩ.


5
Bạn có thể đưa ra một số kịch bản Nevercó thể được sử dụng?
Ramkes Aldama

2
Chúng tôi thực sự đã quyết định sử dụng "? []" Cho toán tử chỉ mục nhận biết null thay vì "?. []". Cái sau phức tạp hơn một chút về mặt ngữ pháp, nhưng đó là những gì người dùng muốn.
khoan hồng

@RamsesAldama Tôi đã thêm một cái gì đó. Các đặc điểm kỹ thuật tôi liên kết để đề cập nhiều hơn.
creativecreatorormaybenot

@munificent Thông số kỹ thuật và thử nghiệm vẫn còn lỗi thời; cảm ơn bạn đã chỉ ra
creativecreatorormaybenot

1
Cần chỉ ra rằng late finaltrên một thành viên hoặc biến thể hiện chỉ kiểm tra khi chạy. Không thể kiểm tra điều đó tại thời điểm phát triển hoặc thời gian biên dịch vì vấn đề tạm dừng. Vì vậy, bạn sẽ không nhận được trợ giúp IDE với nó.
Graham
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.