Có thể null và không thể null
Thuộc phần Lập trình hướng đối tượng trong hành trình Dart của Coddy. Bài học 24 trên 110.
Giờ bạn đã hiểu vì sao tính an toàn với giá trị null lại quan trọng, hãy cùng tìm hiểu cách khai báo biến có thể chứa giá trị null. Trong Dart, bạn thêm ? sau tên kiểu dữ liệu để cho phép kiểu đó nhận giá trị null.
String name = 'Alice'; // Không thể là null, luôn phải có giá trị
String? nickname; // Có thể là null hoặc một String
print(name); // Alice
print(nickname); // nullĐiểm khác biệt chính là kiểu không thể null đảm bảo luôn có giá trị, còn kiểu có thể null cho phép rõ ràng việc không có giá trị. Sự khác biệt này áp dụng cho tất cả các kiểu dữ liệu:
int count = 10; // Phải có giá trị
int? optionalCount; // Có thể là null
List<String> items = []; // Phải là danh sách (có thể rỗng)
List<String>? maybeItems; // Có thể là null (không có danh sách nào)Lưu ý rằng danh sách rỗng [] khác với null. Danh sách rỗng vẫn tồn tại nhưng không chứa gì; null nghĩa là hoàn toàn không có danh sách nào.
Trình biên dịch thực thi nghiêm ngặt các quy tắc này. Bạn không thể gán null cho một biến không thể nhận giá trị null, và không thể dùng một biến có thể nhận giá trị null ở nơi cần biến không thể nhận giá trị null nếu chưa kiểm tra giá trị null:
String? maybeName = 'Bob';
// String definite = maybeName; // Lỗi! Không thể gán giá trị có thể null cho biến không chấp nhận nullTrong bài học tiếp theo, bạn sẽ tìm hiểu các toán tử mà Dart cung cấp để làm việc an toàn với các giá trị có thể là null.
Thử thách
DễHãy xây dựng một hệ thống danh sách phim muốn xem để minh họa sự khác biệt giữa thông tin bắt buộc và thông tin tùy chọn bằng cách dùng các kiểu nullable và non-nullable.
Bạn sẽ tạo hai tệp để sắp xếp code:
movie.dart: Định nghĩa lớpMovieđại diện cho một bộ phim trong danh sách muốn xem. Mỗi phim phải có tiêu đề và năm phát hành (đây luôn là thông tin bắt buộc), nhưng có thể không có một số thông tin bổ sung. Lớp của bạn cần có:String titlenon-nullable, mỗi phim phải có tiêu đềint yearnon-nullable, mỗi phim phải có năm phát hànhString? directornullable, có thể không biết đạo diễndouble? ratingnullable, chưa phải phim nào cũng có điểm đánh giá- Một hàm khởi tạo nhận
titlevàyearlàm tham số bắt buộc - Một phương thức
displayInfo()in thông tin phim, hiển thịUnknownnếu thiếu đạo diễn vàNot ratednếu thiếu điểm đánh giá
main.dart: Import lớp phim của bạn và tạo một danh sách phim ngắn, minh họa cả dữ liệu phim đầy đủ lẫn chưa đầy đủ:- Tạo một phim có tiêu đề
'Inception'và năm2010, sau đó đặt đạo diễn là'Christopher Nolan'và điểm đánh giá là8.8 - Tạo một phim có tiêu đề
'The Matrix'và năm1999, sau đó chỉ đặt điểm đánh giá là8.7(để đạo diễn là null) - Tạo một phim có tiêu đề
'Upcoming Film'và năm2025(để cả đạo diễn lẫn điểm đánh giá là null) - Gọi
displayInfo()cho từng phim theo thứ tự liệt kê ở trên
- Tạo một phim có tiêu đề
Phương thức displayInfo() cần in theo đúng định dạng sau:
[title] ([year])
Director: [director or "Unknown"]
Rating: [rating or "Not rated"]Hãy lưu ý rằng các trường non-nullable (title và year) luôn có giá trị, còn các trường nullable (director? và rating?) có thể không được thiết lập. Điều này phản ánh các tình huống thực tế, trong đó một số dữ liệu là thiết yếu còn một số khác là tùy chọn.
Đầu ra dự kiến:
Inception (2010)
Director: Christopher Nolan
Rating: 8.8
The Matrix (1999)
Director: Unknown
Rating: 8.7
Upcoming Film (2025)
Director: Unknown
Rating: Not ratedTự làm thử
import 'movie.dart';
void main() {
// TODO: Tạo một phim có tiêu đề 'Inception' và năm 2010
// Sau đó đặt director thành 'Christopher Nolan' và rating thành 8.8
// TODO: Tạo một phim có tiêu đề 'The Matrix' và năm 1999
// Sau đó chỉ đặt rating thành 8.7 (giữ director là null)
// TODO: Tạo một phim có tiêu đề 'Upcoming Film' và năm 2025
// Giữ cả director và rating là null
// TODO: Gọi displayInfo() cho từng phim theo thứ tự
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Nền tảng về lập trình hướng đối tượng
Tệp bên ngoàiThư viện và câu lệnh importGiới thiệu về lập trình hướng đối tượngLớp và đối tượngTừ khóa thisPhương thứcBiến thể hiệnKiến thức cơ bản về hàm khởi tạoÔn tập: Máy tính đơn giản4An toàn với giá trị null
Giới thiệu về an toàn với giá trị nullCó thể null và không thể nullToán tử ? và !Từ khóa late và an toàn với giá trị nullToán tử xử lý nullAn toàn với giá trị null trong lớpÔn tập: hệ thống hồ sơ người dùng7Lớp trừu tượng và interface
Lớp trừu tượngPhương thức trừu tượngInterface trong DartInterface ngầm địnhTriển khai và kế thừaNhiều interfaceÔn tập: Máy tính hình10Bộ sưu tập và kiểu tổng quát
Tổng quan về List, Set và MapBộ sưu tập an toàn về kiểuLớp tổng quátPhương thức tổng quátRàng buộc kiểu tổng quátIterable và IteratorÔn tập: Lưu trữ tổng quát2Hàm khởi tạo trong Dart
Hàm khởi tạo mặc địnhHàm khởi tạo có tênDanh sách khởi tạoHàm khởi tạo hằng sốHàm khởi tạo nhà máyHàm khởi tạo chuyển hướngÔn tập: Trình tạo hình dạng5Đóng gói
Thành viên công khai và riêng tưQuy ước tiền tố _Tính riêng tư ở cấp thư việnTìm hiểu sâu về getter và setterChe giấu thông tinÔn tập: hồ sơ học sinh8Mixin
Giới thiệu về MixinTạo MixinSử dụng nhiều MixinTừ khóa on trong MixinMixin và kế thừaMixin và giao diệnÔn tập: hệ thống động vật3Thuộc tính của lớp
Thành viên instance và thành viên tĩnhTrường final và constBiến latePhương thức và trường tĩnhGetter và setterÔn tập: trình quản lý tài khoản ngân hàng6Kế thừa
Kế thừa cơ bảnTừ khóa superGhi đè phương thứcChú thích @overrideTừ khóa lớp finalHàm khởi tạo và kế thừaÔn tập: phân cấp nhân viên9Đa hình
Khái niệm cơ bản về đa hìnhĐa hình qua giao diệnKiểm tra kiểu lúc chạyToán tử is và asTừ khóa hiệp biếnÔn tập: Bộ xử lý thanh toánTự luyện tập: Trình biên dịch Dart online