Menu
CoddyTech

Ràng buộc kiểu tổng quát

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

Đôi khi, bạn cần một kiểu tổng quát có một số khả năng nhất định. Ví dụ, nếu muốn so sánh các phần tử hoặc truy cập các thuộc tính cụ thể, T không bị giới hạn sẽ không đảm bảo rằng những khả năng đó tồn tại. Ràng buộc kiểu tổng quát cho phép bạn giới hạn tham số kiểu ở những kiểu cụ thể hoặc các lớp con của chúng.

Dùng từ khóa extends để giới hạn một tham số kiểu:

class NumberBox<T extends num> {
  T value;
  
  NumberBox(this.value);
  
  T doubled() => (value * 2) as T;
}

void main() {
  var intBox = NumberBox<int>(5);
  var doubleBox = NumberBox<double>(3.5);
  
  print(intBox.doubled());    // 10
  print(doubleBox.doubled()); // 7.0
  
  // var stringBox = NumberBox<String>('hi');  // Lỗi!
}

Vì T extends num, trình biên dịch biết value hỗ trợ các phép toán số học như phép nhân. Nếu không có ràng buộc này, lời gọi value * 2 sẽ thất bại.

Các ràng buộc cũng áp dụng cho hệ thống phân cấp lớp của riêng bạn:

abstract class Animal {
  String get name;
}

class Dog extends Animal {
  String get name => 'Dog';
}

class Shelter<T extends Animal> {
  List<T> animals = [];
  
  void add(T animal) => animals.add(animal);
  
  void printNames() {
    for (var animal in animals) {
      print(animal.name);  // An toàn: T đảm bảo 'name' tồn tại
    }
  }
}

Các ràng buộc tổng quát kết hợp tính linh hoạt của kiểu tổng quát với sự an toàn khi biết những thao tác nào có thể thực hiện trên kiểu dữ liệu của bạn.

challenge icon

Thử thách

Dễ

Hãy xây dựng một máy tính thống kê chỉ hoạt động với các kiểu số! Bạn sẽ tạo một lớp tổng quát có ràng buộc, đảm bảo rằng lớp chỉ có thể dùng với các số, nhờ đó bạn có thể thực hiện các phép toán an toàn trên những giá trị đã lưu.

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

  • stats.dart: Tạo một lớp tổng quát Stats<T extends num> để quản lý danh sách các giá trị số:
    • Một List<T> có tên values để lưu các số
    • Một hàm khởi tạo để khởi tạo danh sách bằng các giá trị được cung cấp
    • Một phương thức sum() trả về tổng của tất cả giá trị dưới dạng num
    • Một phương thức min() trả về giá trị nhỏ nhất dưới dạng T
    • Một phương thức max() trả về giá trị lớn nhất dưới dạng T
    • Một phương thức printStats() hiển thị các số liệu thống kê theo định dạng dễ đọc
  • main.dart: Nhập tệp stats của bạn và minh họa lớp tổng quát có ràng buộc với các kiểu số khác nhau:
    • Tạo một Stats<int> với các giá trị [10, 25, 5, 30, 15] rồi gọi printStats()
    • In ra một dòng trống
    • Tạo một Stats<double> với các giá trị [3.5, 1.2, 4.8, 2.1] rồi gọi printStats()

Phương thức printStats() sẽ in ra ba dòng thể hiện tổng, giá trị nhỏ nhất và giá trị lớn nhất. Vì T extends num, bạn có thể an toàn dùng các toán tử so sánh và phép toán số học với những giá trị này. Điều này sẽ không thể thực hiện được với kiểu tổng quát không có ràng buộc.

Đầu ra dự kiến:

Sum: 85
Min: 5
Max: 30

Sum: 11.6
Min: 1.2
Max: 4.8

Tự làm thử

import 'stats.dart';

void main() {
  // TODO: Tạo Stats<int> với các giá trị [10, 25, 5, 30, 15]
  // và gọi printStats()
  
  // TODO: In ra một dòng trống
  
  // TODO: Tạo Stats<double> với các giá trị [3.5, 1.2, 4.8, 2.1]
  // và gọi printStats()
}
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