[Dart&Flutter][Bài3]Biến và kiểu dữ liệu trong Dart (P.2)

[Dart&Flutter][Bài3]Biến và kiểu dữ liệu trong Dart (P.2)

Trong bài trước, mình đã giới thiệu về biến trong Dart. Hôm nay, mình sẽ giới thiệu tiếp về kiểu dữ liệu:

2. Kiểu dữ liệu:

2.1. Số:

Dart có hai kiểu dữ liệu số:

  • int: 64 bit, khai báo giá trị số nguyên, nằm trong khoảng -263  đến -263 -1
  • double: 64 bit , khai báo số thập phân, theo tiêu chuẩn IEEE 754 cổ điển

Cả intdouble là lớp con của num, bao gồm nhiều phương thức nhưL

  • parse()
  • abs()
  • ceil()
  • toString()
  • ........

Các bạn sẽ thường xuyên sử dụng intdouble. Trong một số trường hợp đặc biệt, khi kiểu dữ liệu không rõ ràng, chúng ta vẫn dùng num, nhưng nhìn chung nên tránh việc sử dụng khai báo num.

var a = 1; //int
var b = 4.0 //double

int a = 4;
double b = y - 4;
double b = 5;

Từ phiên bản Dart 2.1, việc khai báo double b = 5 hoàn toàn đúng quy tắc. Trước ấy, việc khai báo double bắt buộc phải là 5.0, do trình biên dịch không tự động chuyển đổi các giá trị. Một số lưu ý đặc biệt có thể sẽ có hữu ích cho bạn:

  • Biểu diễn theo cấp số nhân của một số, chẳng hạn như var a = 1.35e2, là tương đương của 1.35 * 102
  • Biểu diễn thập lục phân của một số, chẳng hạn như var a = 0xF1A trong đó 0xF1A bằng F1A trong cơ số 16 (3866 trong cơ số 10).

2.1.1. Luyện tập

  • Parse các giá trị:
String value = '17';

var a = int.parse(value); //String to int
var b = double.parse(value); //String to double
var c = int.parse('13', radix:6); // chyển đổi từ 13 cơ số 6

Trong quá trình parse()  các giá trị, tránh việc đầu vào không định dạng, ví dụ như số 12a34, gây ra các vấn đề biên dịch, chúng ta nên sử dụng một trong số cách sau:

  • sử dụng tryParse():
double? value = double.tryParse('12a34'); // null
  • Sử dụng onError trong parse():
var a = int.parse("1_6", onError: (value) => 0); // 0
var a = int.parse("16", onError: (value) => 0); // 16

2.2. Chuỗi:

Trong Dart, một chuỗi bao gồm các kí tự UTF-16 được bao quanh bởi dấu ngoặc đơn hoặc ngoặc kép. Một tính năng rất hay của Dart là nó có thể kết hợp các biểu thức thành chuỗi bằng cách sử dụng ${value} ( một cách viết của toString()).

// cả s và t đều là chuỗi, k có sự khác biệt nào
var s = "Double quoted";
var t = 'Single quoted';

// đưa một số nguyên vào chuỗi
var number = 25;
var myAge = "có $number quả táo"; // 'có 25 quả táo'

// cần sử dụng {} trong các biểu thức
var test = "${25.abs()}"

// không cần thiết, ${} chính là gọi toString()
var redundant = "${25.toString()}";

Một chuỗi có thể đơn hoặc đa dòng. Chuỗi đơn dòng được biểu diễn bằng ngoặc đơn hoặc kép, chuỗi đa dòng được diễn bằng ba lần ngoặc. Điều này hữu ích giúp định dạng code dễ đọc.

var query = """
	SELECT name, surname, age
	FROM people
	WHERE age >= 18
	ORDER BY name DESC
""";

Trong Dart không có kiểu char. Tất cả đều là các chuỗi. Nếu bạn muốn tiếp cận các kí tự của chuỗi, sử dụng toán tử [ ]:

final name = "Alberto";

print(name[0]); // prints "A"
print(name[2]); // prints "b";

giá trị trả về của name[0] là một String và độ dài là 1.

Bạn cũng có thể nối các chuỗi dễ dàng với toàn tử +, một cách được hỗ trợ bởi rất nhiều ngôn ngữ khác

var s = 'Tôi rất là ' + ' đẹp trai ';

Trong trường hợp chuỗi dài quá một dòng đơn, tránh dùng toán tử + mà hãy xuống dòng. Nó có ý nghĩa về mặt thẩm mĩ hơn là mặt hiệu suất

var s = 'Tôi làm ở'
	'Pirago';


var s = 'Tôi làm ở' +
	'Pirago';
        
//cả hai cùng trả về một kết quả nhưng phía trên code sẽ đẹp hơn

Bởi vì chuỗi là bất biến, thế nên việc sử dụng quá nhiều toán tử + có thể không có tác dụng. Trong trường hợp đó ta nên sử dụng StringBuffer để nối chuỗi. Ví dụ:

var value = "";

for(var i = 0; i < 900000; ++i) {
	value += "$i ";
}

Mỗi lần toán tử + được gọi, giá trị được gán với một instance mới được kết hợp bởi cả giá trị mới và cũ. Hay nói cách khác, dòng code tạo ta 90000 lần một đối tưởng String mới, một cho mỗi lần lặp và nó không tối ưu chút nào. Thay vì thế, chúng ta sẽ làm như sau:

for(var i = 0; i < 900000; ++i)
	buffer.write("$i ");

var value = buffer.toString();

Code trên tốt hơn rất nhiều vì StringBuffer không tạo ra một chuỗi mới mỗi lần lặp. Chuỗi được tạo chỉ một lần ở thời điểm toString() được gọi. Khi bạn phải dùng vòng lặp dài mà thao tác với chuỗi, tránh dùng toán tử + mà thay vào đó dùng một buffer. Nó tương tự Java.

2.3. Kiểu dữ liệu được liệt kê (Enum)

Kiểu dữ liệt được liệt kê là một ô chứa các các hằng số mà có thể được khai báo với từ khoá enum. Ví dụ:

enum Fruits { Apple, Pear, Grapes, Banana, Orange }

void main() {
var a = Fruits.Apple.index; // 0
var b = Fruits.Pear.index; // 1
var c = Fruits.Grapes.index; // 2
}

Chú ý khi sử dụng enum bạn luôn cần phải đáp ứng đầy đủ điểu kiện của nó. Chỉ dùng tên thì không hoạt động.

Khi bạn cần một danh sách giá trị được xác định trước đại diện cho một số loại dữ liệu dạng văn bản hoặc số, bạn nên sử dụng enum hơn một kiểu dữ liệu nguyên thuỷ. Nó giúp code dễ đọc hơn, tính nhất quán của code và kiểm tra thời gian biên dịch.

enum Chess { King, Queen, Rook, Bishop, Knight, Pawn }

/// METHOD 1. kiểm tra quân cờ nào đi theo đường chéo.
bool diagonalMoveC(Chess item) { ... }

/// METHOD 2. Kiểm tra xem quân cờ nào có thể đi theo đường chéo: [item] chỉ có thể là:
/// 1. King
/// 2. Queen
/// 3. Rook
/// 4. Bishop
/// 5. Knight
/// 6. Pawn
/// bất kì số nào khác đều k được.
bool diagonalMoveS(int item) { ... }

Ví dụ này cho thấy,  phương thức thứ nhất là lựa chọn đúng.

  • diagonalMoveC(Chess item) có một lợi thế là đảm bảo rằng item chỉ có thể là một trong các giá trị của Chess. Không cần kiểm tra cụ thể và chắc chắn code sẽ được chạy qua ngay
  • diagonalMoveS(int item) có một bất lợi là chúng ta có thể truyền vào bất kì số nào, ngoài khoảng 1-6 cũng được. Do đó sẽ phải thêm phần kiểm tra giá trị truyền vào xem có chứa giá trị hợp lệ.

2.4. Booleans

Bạn có thể gán giá trị tới kiểu bool chỉ với giá trị true hoặc false, cả hai đều là hằng số trong thời gian biên dịch.

bool test = 5 == 0; // false
bool test2 = !test; // giá trị ngược lại giá trị test (true)

2.5 Mảng (Array)

Trong Java có thể tạo mảng với int[] array = new int[5]; Nhưng trong Dart, chỉ có thể tạo với một colllections: một mảng trong Dart được thay thế bởi List<T> (T có thể là bất cứ kiểu dữ liệu nào).

JAVA:
// 1. Mảng
double[] test = new test[10];
// 2. Danh sách
List<double> test = new ArrayList<>();

• Dart
// 1. Mảng
// (không)
// 2.Danh sách
List<double> test = new List<double>();


Trong Dart, bạn có thể làm việc với các mảng nhưng chúng được hiểu là các đại diện của List<T>. List là collections  và các item được lập chỉ mục 0 có thể được truy cập ngẫu nhiên bằng toán tử [], toán tử này sẽ ném một ngoại lệ nếu bạn vượt quá giới hạn.

//dùng var hoặc final
final myList = [-3.1, 5, 3.0, 4.4];
final value = myList[1];

Có nhiều phương thức hỗ trợ cho List<T>:

  • length,
  • add(T value),
  • isEmpty,
  • contains(T value)

..........

Bài blog hôm nay mình đã giới thiệu về các kiểu dữ liệu trong Dart, trong bài tới, mình sẽ giới thiệu về Null Safety và các toán tử trong Dart.

Tham khảo: Flutter Complete Reference