Menu
CoddyTech

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à null

Dù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.

challenge icon

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ớp Product đạ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 name không thể nhận null, mọi sản phẩm đều phải có tên
    • double price không thể nhận null, mọi sản phẩm đều phải có giá
    • String? description có thể nhận null, một số sản phẩm chưa có mô tả
    • String? category có 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 name và price là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ề null nế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 description khi mô tả là null và Uncategorized khi 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(), in Description length: [length] bằng getDescriptionLength(), và in Category: [uppercase] bằng getCategoryUppercase()
    • Với Mystery Item: gọi displayProduct() và in Description length: [length] bằng getDescriptionLength() (kết quả sẽ là null)

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: null

Tự 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()
}
quiz iconTự kiểm tra

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

Tự luyện tập: Trình biên dịch Dart online