Toán tử is và as
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 60 trên 110.
Trong khi runtimeType cho bạn biết kiểu chính xác, Dart cung cấp hai toán tử thiết thực hơn để kiểm tra kiểu (type checking) và ép kiểu (casting): is và as.
Toán tử is kiểm tra xem một đối tượng có thuộc một kiểu cụ thể hay không và trả về giá trị boolean. Khác với runtimeType, toán tử này cũng trả về true cho các kiểu cha:
class Animal {}
class Dog extends Animal {}
void main() {
Animal pet = Dog();
print(pet is Dog); // true
print(pet is Animal); // true (Dog là một Animal)
print(pet is String); // false
}Khi bạn dùng is bên trong câu lệnh if, Dart tự động nâng cấp kiểu, cho phép bạn truy cập các thành viên của lớp con mà không cần ép kiểu tường minh:
void handleAnimal(Animal animal) {
if (animal is Dog) {
// Ở đây, Dart biết animal là một Dog
animal.bark(); // Có thể gọi các phương thức (method) của riêng Dog
}
}Toán tử as chuyển đổi tường minh một đối tượng sang một kiểu cụ thể. Hãy dùng toán tử này khi bạn chắc chắn về kiểu:
Animal pet = Dog();
Dog myDog = pet as Dog; // Ép kiểu sang Dog
myDog.bark();Hãy cẩn thận với as: nếu ép kiểu thất bại, nó sẽ gây ra ngoại lệ. Vì vậy, kết hợp is với việc nâng cấp kiểu thường an toàn hơn dùng trực tiếp as. Bạn cũng có thể dùng is! để kiểm tra xem một đối tượng có không thuộc một kiểu nhất định hay không.
Thử thách
DễHãy xây dựng một hệ thống xử lý hình dạng dùng các toán tử is và as để nhận diện và làm việc với các loại hình dạng khác nhau. Bạn sẽ tạo một hệ thống phân cấp các hình dạng và viết các hàm kiểm tra kiểu dữ liệu, đồng thời truy cập an toàn vào các thuộc tính riêng của từng kiểu.
Bạn sẽ sắp xếp code vào hai tệp:
shapes.dart: Định nghĩa hệ thống phân cấp hình dạng tại đây:- Một lớp
Shapecó thuộc tínhString namevà hàm khởi tạo. Thêm phương thứcdescribe()để in ra[name] is a shape - Một lớp
Circlekế thừaShapevà có thêm thuộc tínhdouble radius. Thêm phương thứcgetCircleInfo()để in raCircle with radius: [radius] - Một lớp
Rectanglekế thừaShapevà có thêm các thuộc tínhdouble widthvàdouble height. Thêm phương thứcgetRectangleInfo()để in raRectangle: [width] x [height]
- Một lớp
main.dart: Nhập tệp hình dạng và tạo hàmprocessShape(Shape shape)để:- Gọi
describe()trên hình dạng trước tiên - Dùng
isđể kiểm tra hình dạng có phải làCirclekhông. Nếu đúng, dùng cơ chế nâng kiểu tự động để gọigetCircleInfo() - Dùng
isđể kiểm tra hình dạng có phải làRectanglekhông. Nếu đúng, dùng toán tửasđể ép kiểu rồi gọigetRectangleInfo() - Dùng
is!để kiểm tra hình dạng KHÔNG phải làCirclevà KHÔNG phải làRectangle. Nếu đúng, in raUnknown shape type
main():- Tạo một
Circlecó tên'Round', bán kính5.0, rồi truyền nó vàoprocessShape() - In ra một dòng trống
- Tạo một
Rectanglecó tên'Box', chiều rộng4.0và chiều cao3.0, rồi truyền nó vàoprocessShape() - In ra một dòng trống
- Tạo một
Shapecơ sở có tên'Mystery', rồi truyền nó vàoprocessShape()
- Gọi
Thử thách này minh họa cả hai cách: dùng is với cơ chế nâng kiểu tự động cho Circle, và dùng as để ép kiểu tường minh cho Rectangle. Bạn cũng sẽ thấy is! giúp nhận diện những hình dạng không thuộc bất kỳ kiểu con cụ thể nào.
Đầu ra mong đợi:
Round is a shape
Circle with radius: 5.0
Box is a shape
Rectangle: 4.0 x 3.0
Mystery is a shape
Unknown shape typeTự làm thử
import 'shapes.dart';
// TODO: Tạo hàm (function) processShape(Shape shape) thực hiện các bước sau:
// 1. Đầu tiên, gọi describe() trên shape
// 2. Dùng 'is' để kiểm tra shape có phải là Circle không. Nếu đúng, dùng cơ chế nâng kiểu để gọi getCircleInfo()
// 3. Dùng 'is' để kiểm tra shape có phải là Rectangle không. Nếu đúng, dùng toán tử 'as' để ép kiểu rồi gọi getRectangleInfo()
// 4. Dùng 'is!' để kiểm tra shape không phải là Circle và cũng không phải là Rectangle. Nếu đúng, in ra "Unknown shape type"
void main() {
// TODO: Tạo một Circle tên 'Round' có bán kính 5.0 rồi truyền vào processShape()
// TODO: In ra một dòng trống
// TODO: Tạo một Rectangle tên 'Box' có chiều rộng 4.0 và chiều cao 3.0 rồi truyền vào processShape()
// TODO: In ra một dòng trống
// TODO: Tạo một Shape cơ sở tên 'Mystery' rồi truyền vào processShape()
}
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