Toán tử ? và !
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 25 trên 110.
Khi làm việc với các kiểu dữ liệu có thể nhận giá trị null, Dart cung cấp hai toán tử thiết yếu để truy cập giá trị an toàn. Toán tử truy cập có xét đến null ?. và toán tử khẳng định không null ! giúp bạn kiểm soát cách xử lý các giá trị có thể là null.
Toán tử ?. truy cập an toàn vào thuộc tính hoặc phương thức của một giá trị có thể là null. Nếu giá trị là null, toàn bộ biểu thức trả về null thay vì gây lỗi:
String? name = 'Alice';
print(name?.length); // 5
name = null;
print(name?.length); // null (không bị lỗi!)Toán tử ! cho Dart biết: “Mình chắc chắn giá trị này không phải là null.” Toán tử này chuyển kiểu có thể null thành kiểu không thể null, nhưng sẽ gây ra lỗi nếu giá trị thực sự là null:
String? maybeName = 'Bob';
String definite = maybeName!; // OK, chúng ta khẳng định giá trị này không phải null
print(definite.length); // 3
String? empty = null;
// String crash = empty!; // Lỗi khi chạy! Giá trị là nullDùng ?. khi bạn muốn xử lý giá trị null một cách an toàn. Chỉ dùng ! khi bạn hoàn toàn chắc chắn rằng một giá trị tồn tại, có thể sau khi kiểm tra bằng câu lệnh if hoặc khi bạn biết logic đảm bảo có giá trị. Dùng ! quá nhiều sẽ làm mất mục đích của tính năng an toàn với null và có thể gây ra sự cố khi chạy.
Thử thách
DễHãy xây dựng hệ thống kiểm kê sản phẩm để minh họa cách làm việc an toàn với các giá trị có thể null bằng toán tử ?. và !.
Bạn sẽ tạo hai tệp để sắp xếp code:
product.dart: Định nghĩa lớpProductđại diện cho một mặt hàng trong kho. Một số sản phẩm có đầy đủ thông tin, còn những sản phẩm khác vẫn đang được lập danh mục. Lớp của bạn cần có:String namekhông thể nhận null, mọi sản phẩm đều phải có têndouble pricekhông thể nhận null, mọi sản phẩm đều phải có giáString? descriptioncó thể nhận null, một số sản phẩm chưa có mô tảString? categorycó thể nhận null, một số sản phẩm chưa được phân loại- Một hàm khởi tạo nhận
namevàpricelàm tham số bắt buộc - Một phương thức
getDescriptionLength()trả về độ dài của mô tả bằng?., hoặc trả vềnullnếu không có mô tả - Một phương thức
getCategoryUppercase()trả về danh mục viết hoa bằng!. Phương thức này giả định rằng danh mục đã được đặt - Một phương thức
displayProduct()in thông tin sản phẩm, hiển thịNo descriptionkhi mô tả là null vàUncategorizedkhi danh mục là null
main.dart: Nhập lớp sản phẩm và minh họa cả hai toán tử:- Tạo sản phẩm có tên
'Laptop'với giá999.99, sau đó đặt mô tả thành'High-performance computing device'và danh mục thành'Electronics' - Tạo sản phẩm có tên
'Mystery Item'với giá49.99, để mô tả và danh mục là null - Với Laptop: gọi
displayProduct(), inDescription length: [length]bằnggetDescriptionLength(), và inCategory: [uppercase]bằnggetCategoryUppercase() - Với Mystery Item: gọi
displayProduct()và inDescription length: [length]bằnggetDescriptionLength()(kết quả sẽ lànull)
- Tạo sản phẩm có tên
Phương thức displayProduct() cần in theo định dạng này:
Product: [name] - $[price]
Description: [description or "No description"]
Category: [category or "Uncategorized"]Đầu ra dự kiến:
Product: Laptop - $999.99
Description: High-performance computing device
Category: Electronics
Description length: 32
Category: ELECTRONICS
Product: Mystery Item - $49.99
Description: No description
Category: Uncategorized
Description length: nullTự làm thử
import 'product.dart';
void main() {
// TODO: Tạo một sản phẩm tên 'Laptop' có giá 999.99
// Sau đó đặt mô tả của sản phẩm thành 'High-performance computing device'
// Và đặt danh mục của sản phẩm thành 'Electronics'
// TODO: Tạo một sản phẩm tên 'Mystery Item' có giá 49.99
// Giữ mô tả và danh mục ở giá trị null
// TODO: Với Laptop:
// - Gọi displayProduct()
// - In ra "Description length: [length]" bằng getDescriptionLength()
// - In ra "Category: [uppercase]" bằng getCategoryUppercase()
// TODO: Với Mystery Item:
// - Gọi displayProduct()
// - In ra "Description length: [length]" bằng getDescriptionLength()
}
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