Menu
CoddyTech

Trường final và const

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 18 trên 110.

Khi khai báo các trường trong một lớp, bạn có thể dùng final hoặc const để kiểm soát việc giá trị có thể thay đổi sau khi khởi tạo hay không. Cả hai đều tạo ra các giá trị bất biến, nhưng chúng hoạt động khác nhau.

Trường final chỉ có thể được gán một lần, nhưng giá trị của nó được xác định khi chạy chương trình. Bạn có thể gán giá trị trong phần khai báo hoặc thông qua hàm khởi tạo:

class Person {
  final String name;
  final DateTime createdAt = DateTime.now();
  
  Person(this.name);
}

Person p = Person('Alice');
// p.name = 'Bob';  // Lỗi: không thể gán lại giá trị cho trường final

name được thiết lập khi đối tượng được tạo, còn createdAt ghi lại thời điểm hiện tại. Cả hai giá trị đều bị khóa sau khi khởi tạo.

Trường static const phải có giá trị xác định được tại thời điểm biên dịch. Trường này thuộc về chính lớp và được dùng chung cho mọi đối tượng:

class Circle {
  static const double pi = 3.14159;
  final double radius;
  
  Circle(this.radius);
  
  double get area => Circle.pi * radius * radius;
}

Điểm khác biệt chính: các thuộc tính final có thể chứa giá trị được tính khi chạy (như DateTime.now()), còn const yêu cầu hằng số tại thời điểm biên dịch. Thuộc tính instance không thể trực tiếp là const, chỉ có thể dùng static const ở cấp lớp.

challenge icon

Thử thách

Dễ

Hãy xây dựng một hệ thống danh mục sản phẩm để minh họa sự khác biệt giữa các trường final và static const. Bạn sẽ tạo các sản phẩm có thuộc tính bất biến và các giá trị cấu hình dùng chung.

Bạn sẽ sắp xếp code thành hai tệp:

  • product.dart: Định nghĩa một lớp Product đại diện cho một mặt hàng trong cửa hàng. Lớp của bạn cần có:
    • Một static const double taxRate có giá trị 0.08 (thuế suất 8% dùng chung cho tất cả sản phẩm)
    • Một static const String currency có giá trị 'USD'
    • Một final String name lưu tên sản phẩm (được thiết lập qua hàm khởi tạo)
    • Một final double basePrice lưu giá gốc của sản phẩm (được thiết lập qua hàm khởi tạo)
    • Một final String sku (đơn vị lưu kho), kết hợp tiền tố với tên sản phẩm, được tính trong danh sách khởi tạo bằng cách thêm tên viết hoa vào sau 'SKU-'
    • Một hàm khởi tạo nhận name và basePrice
    • Một getter priceWithTax tính giá đã bao gồm thuế
    • Một phương thức display() in thông tin sản phẩm ra
  • main.dart: Nhập lớp sản phẩm và tạo hai sản phẩm:
    • Một sản phẩm 'Laptop' có giá gốc là 999.99
    • Một sản phẩm 'Mouse' có giá gốc là 29.99
    Gọi display() cho từng sản phẩm theo thứ tự liệt kê ở trên.

Phương thức display() cần in ra theo đúng định dạng sau:

[sku] - [name]: [basePrice] [currency] (with tax: [priceWithTax] [currency])

Khi hiển thị, làm tròn priceWithTax đến 2 chữ số thập phân bằng toStringAsFixed(2).

Kết quả mong đợi:

SKU-LAPTOP - Laptop: 999.99 USD (with tax: 1079.99 USD)
SKU-MOUSE - Mouse: 29.99 USD (with tax: 32.39 USD)

Tự làm thử

import 'product.dart';

void main() {
  // TODO: Tạo một Product tên là 'Laptop' với giá cơ bản 999.99
  
  // TODO: Tạo một Product tên là 'Mouse' với giá cơ bản 29.99
  
  // TODO: Gọi display() cho từng sản phẩm theo thứ tự (Laptop trước, rồi đến Mouse)
  
}
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