Hướng dẫn và ví dụ Dart Generics
Trong lập trình Dart, có thể bạn đã quen thuộc với lớp List và Map và quen thuộc với ký hiệu List<E> và Map<K,V>. Trong trường hợp này List<E> và Map<K,V> được gọi là Generics Type hoặc Parameterized Type (kiểu được tham số hoá).
Các ký hiệu E, K, V,.. được gọi là các tham số Generics. Theo quy ước các tham số Generics là các ký tự viết hoa.
Tại sao nên sử dụng Generics:
Generics giúp trình biên dịch phát hiện ra các lỗi liên quan tới dữ liệu tại thời điểm biên dịch, giúp giảm thiểu lỗi phát sinh trong quá trình chạy của chương trình. Trong thực tế Generics còn mang lại nhiều lợi ích khác.
- Việc chỉ định đúng kiểu Generics giúp mã được tạo ra tốt hơn.
- Bạn có thể sử dụng Generics làm giảm sự trùng lặp mã.
Ví dụ, bạn muốn có một danh sách chỉ chứa các phần tử kiểu String, bạn sẽ sử dụng List<String>, trình biên dịch sẽ thông báo lỗi nếu bạn vô tình thêm một phần tử không phải String vào danh sách.
void main() {
List<String> myList= ["Tom", "Jerry"];
myList.add("Donald"); // OK
myList.add(10); // ==> ERROR!
}
Trong bài viết này chúng ta sẽ thảo luận làm thế nào để tạo ra các lớp, hàm và phương thức Generics.
1. Lớp Generics
Trong ví dụ dưới đây, chúng ta định nghĩa một lớp Generics - KeyValue<K,V>:
key_value.dart
class KeyValue<K,V> {
K key;
V value;
KeyValue(this.key, this.value);
}
Khi sử dụng lớp KeyValue<K,V> bạn phải chỉ định kiểu dữ liệu cụ thể cho <K,V>, chẳng hạn:
- K = String
- V = int
key_value_demo.dart
import 'key_value.dart';
void main() {
// K is String
// V is int
KeyValue<String, int> tomSalary = KeyValue("Tom", 1000);
// key is String
String name = tomSalary.key;
// value is int
int salary = tomSalary.value;
print("Name: $name");
print("Salary: $salary");
}
Output:
Name: Tom
Salary: 1000
2. Thừa kế từ một lớp Generics (1)
Bạn có thể tạo một lớp con thừa kế từ một lớp Generics và chỉ định rõ kiểu cụ thể cho tham số Generics.
Trong ví dụ này, lớp NameSalaryEntry là một lớp con của lớp KeyValue<K,V> với tham số <K,V> được chỉ định kiểu cụ thể.
- K = String (Name)
- V = int (Salary)
name_salary_entry.dart
import 'key_value.dart';
/// This class extends from KeyValue<K,V>
///
/// K = String - Name of a person
/// V = int - Salary of a person
///
class NameSalaryEntry extends KeyValue<String, int> {
///
/// Constructor to assign values to superclass properties
///
NameSalaryEntry(super.key, super.value);
}
Sử dụng lớp NameSalaryEntry:
name_salary_entry_demo.dart
import 'name_salary_entry.dart';
void main() {
var tom = NameSalaryEntry("Tom", 1000);
String name = tom.key; // K is a String
int salary = tom.value; // V is an int
print("Person Name: $name");
print("Person Salary: $salary");
}
3. Thừa kế từ một lớp Generics (2)
Bạn cũng có thể tạo một lớp thừa kế từ một lớp Generics và chỉ định kiểu cụ thể cho một vài tham số Generics. Chẳng hạn, lớp StringVEntry dưới đây mở rộng từ lớp KeyValue<K,V> với tham số <K> là <String>.
string_v_entry.dart
import 'key_value.dart';
///
/// This class extends from KeyValue<K,V> with:
/// K = String
///
class StringVEntry<V> extends KeyValue<String, V> {
///
/// Constructor to assign values to superclass properties
///
StringVEntry(super.key, super.value);
}
StringVEntry<V> là một lớp Generics với chỉ một tham số Generics.
string_v_entry_demo.dart
import 'package:generics/string_v_entry.dart';
void main() {
///
/// V = double - Salary of a Person
///
StringVEntry<double> tom = StringVEntry<double>("Tom", 1001.1);
String personName = tom.key;
double personSalary = tom.value;
print("Person Name: $personName");
print("Person Salary: $personSalary");
}
Output:
Person Name: Tom
Person Salary: 1001.1
4. Thừa kế từ một lớp Generics (3)
Ví du, tạo một lớp con có 3 tham số Generics mở rộng từ một lớp có 2 tham số Generics:
key_value_info.dart
import 'key_value.dart';
///
/// This class extends KeyValue<K,V> class and has additional Generics <I> parameter.
///
class KeyValueInfo<K, V, I> extends KeyValue<K, V> {
I info;
KeyValueInfo(super.key, super.value, this.info);
}
5. Phương thức Generics (1)
Các tham số Generics có thể tham gia trong một phương thức.
generics_method_example.dart
class Team<E> {
List<E> members;
///
/// Constructor:
///
Team(this.members);
E? getFirstMember() {
if (members.isNotEmpty) {
return members[0];
}
return null;
}
E? getLastMember() {
if (members.isNotEmpty) {
return members[members.length - 1];
}
return null;
}
}
void main() {
List<String> list = ["Tom", "Jerry", "Donald"];
Team<String> myTeam = Team(list);
String? firstMember = myTeam.getFirstMember();
print("First Member: $firstMember");
String? lastMember = myTeam.getLastMember();
print("Last Member: $lastMember");
}
Output:
First Member: Tom
Last Member: Donald
6. Phương thức Generics (2)
Thông thường các tham số Generics được định nghĩa cùng với tên lớp. Tuy nhiên, nó cũng có thể được định nghĩa trực tiếp bởi phương thức hoặc hàm.
my_utils.dart
import 'key_value.dart';
class MyUtils {
///
/// Define static method with Generics parameters <K,V>.
/// getKey<K,V>(parameters)
///
static K getKey<K, V>(KeyValue<K, V> kv) {
return kv.key;
}
///
/// Define static method with Generics parameter <E>.
/// getFirstElement<E>(parameters)
///
static E? getFirstElement<E>(List<E> list) {
if (list.isEmpty) {
return null;
}
return list[0];
}
}
Kiểu của tham số Generics sẽ được chỉ định tại thời điểm gọi phương thức (hoặc hàm).
my_utils_demo.dart
import 'my_utils.dart';
void main() {
List<String> names = ["Tom", "Jerry", "Donald"];
String? firstName = MyUtils.getFirstElement(names);
print("First Element: $firstName");
}
Output:
First Element: Tom
7. Hàm Generics
Ví dụ, một hàm Generics với một tham số Generics:
generics_function_example.dart
///
/// A Generics Function:
///
F? getLastElement<F>(List<F> list) {
if (list.isEmpty) {
return null;
}
return list[0];
}
void main() {
List<int> numbers = [11, 22, 33];
int? lastNumber = getLastElement(numbers);
print("Last Number: $lastNumber"); // 33
}
Các hướng dẫn lập trình Dart
- Kiểu dữ liệu Boolean trong Dart
- Hướng dẫn và ví dụ hàm trong Dart
- Hướng dẫn và ví dụ Dart Closures
- Interface trong Dart
- Hướng dẫn và ví dụ phương thức trong Dart
- Constructor trong Dart
- Property trong Dart
- Toán tử chấm chấm (..) trong Dart
- Hướng dẫn và ví dụ Dart Generics
- Lập trình Dart với công cụ trực tuyến DartPad
- Cài đặt Dart SDK trên Windows
- Cài đặt Visual Studio Code trên Windows
- Cài đặt Dart Code Extension cho Visual Studio Code
- Cài đặt Dart Plugin cho Android Studio
- Chạy ví dụ Dart đầu tiên của bạn trong Visual Studio Code
- Chạy ví dụ Dart đầu tiên của bạn trong Android Studio
- Dart JSON với thư viện dart:convert
- Hướng dẫn và ví dụ Dart List
- Biến (Variable) trong ngôn ngữ Dart
- Hướng dẫn và ví dụ Dart Map
- Vòng lặp trong Dart
- Xử lý Dart JSON với gói dart_json_mapper
- Trình chuyển đổi mã nguồn (Transpiler) là gì?
- Phân tích XML trong Dart
- Hướng dẫn và ví dụ Dart http
- Hướng dẫn và ví dụ Dart Future
- Các phương thức mở rộng (Extension) trong Dart
- Mixins trong Dart
- Bài thực hành Dart phân tích JSON với gói dart:convert
- Bài thực hành Dart http CRUD
- Từ khoá part và part of trong Dart
- Hướng dẫn và ví dụ Dart Dio
Show More