Skip to main content

Dart - 型別

warning

Dart 的紀錄無限期停更,因為我離開要我寫 Flutter 的公司了,所以 Dart 就等我哪天有興趣再繼續寫吧! 最近的興趣再 C# 跟 Unity 上~ 😎

info

線上練習網站 DartPad

Dart 中的型別

基本型別如下述:

英文名中文名型別簡寫型別描述簡例
Numbers數字int整數int number = 10;
double浮點數double pi = 3.14;
Strings字符串String文本信息String message = 'Hello, world!';
Booleans布林值bool表示 true 或 false 的值bool isTrue = true;
Records紀錄(value1, value2)用於捕獲和傳遞多個值var record = (1, 2);
Lists列表List有序集合,其實就是陣列List<int> numbers = [1, 2, 3];
Sets集合Set無序且唯一的元素集合Set<String> uniqueWords = {'apple', 'banana', 'orange'};
Maps映射Map鍵-值對的集合Map<String, int> ages = {'John': 30, 'Alice': 25};
Runes字符Runes (有取代用法)用於表示 Unicode 字符序列var hi = 'Hi \u2665';
Symbols符號Symbol表示 Dart 程式碼中的識別符Symbol symbol = #mySymbol;
null空值Null表示沒有任何值的特殊類型var myVar = null;

特殊的型別如下述:

英文名中文名型別簡寫型別描述簡例
Object對象Object所有 Dart 類的超類,除了 NullObject obj = new Object();
Enum枚舉Enum所有枚舉的超類enum Color { red, green, blue }
Future未來Future用於非同步Future<int> future = Future.delayed(Duration(seconds: 1), () => 42);
StreamStream用於非同步Stream<int> stream = Stream.fromIterable([1, 2, 3]);
Iterable可迭代Iterablefor-in 循環和同步生成器函數中使用Iterable<int> numbers = [1, 2, 3];
Never永不Never表示一個表達式永遠不會成功完成評估,多用在長拋出異常的函式Never neverEndingFunction() { while(true) {} }
dynamic動態dynamic表示要禁用靜態檢查,通常應該使用 ObjectObject? 取代此型別使用dynamic value = 10;
voidvoid表示值永遠不會使用,通常用作返回類型void function() { print('Hello'); }
info

Superclass (超類別) 這是一個 OOP 的術語,指的是一個類別的直接父類。
一個類別可以繼承它的超類的屬性和方法,並且可以在其自身定義新的屬性和方法。

舉例來說,假設有一個類別 Animal 作為超類,而 Dog 是一個繼承自 Animal 的子類。在這個情況下,Animal 就是 Dog 的超類。

class Animal {
void eat() {
print('Animal is eating');
}
}

class Dog extends Animal {
void bark() {
print('Dog is barking');
}
}

void main() {
var dog = Dog();
dog.eat(); // 繼承自超類 Animal 的方法
dog.bark(); // Dog 自身定義的方法
}

Numbers

基本上數字的型別就是整數 (int) 跟浮點數 (double),但也可以透過 num 定義一個變數同時擁有整數跟浮點數性質:

num x = 1; // x can have both int and double values

數字轉其他型別

下述為官方數字轉其他型別的範例:

// 字串 -> 整數
var one = int.parse('1');
assert(one == 1);

// 字串 -> 浮點數
var onePointOne = double.parse('1.1');
assert(onePointOne == 1.1);

// 整數 -> 字串
String oneAsString = 1.toString();
assert(oneAsString == '1');

// 浮點數 -> 字串
String piAsString = 3.14159.toStringAsFixed(2);
assert(piAsString == '3.14');

Strings

同樣作為最常見資料型別,宣告一個字串型別只需要使用 ''""

var s1 = 'Single quotes work well for string literals.';
var s2 = "Double quotes work just as well.";

字符串插值(String Interpolation)

透過 ${expression} 的形式,可以將表達式的值插入到一個字符串中。
若該表達式是一個識別符 (identifier),可以省略 {}

var s = 'string interpolation';

print('Dart has $s, which is very handy.'); // Dart has string interpolation, which is very handy.
print('${s.toUpperCase()} is very handy!'); // STRING INTERPOLATION is very handy!
warning

注意常量字符串的使用:

// These work in a const string.
const aConstNum = 0;
const aConstBool = true;
const aConstString = 'a constant string';

// These do NOT work in a const string.
var aNum = 0;
var aBool = true;
var aString = 'a string';
const aConstList = [1, 2, 3];

const validConstString = '$aConstNum $aConstBool $aConstString';
// const invalidConstString = '$aNum $aBool $aString $aConstList';

在這個例子中,aConstNumaConstBoolaConstString 都是編譯時常量,因此可以在常量字符串中使用。
但是,aNumaBoolaString 不是編譯時常量,因此不能在常量字符串中使用。
aConstList 是一個常量列表,但它包含的不是編譯時常量的值,因此也不能在常量字符串中使用。
因此,validConstString 是一個有效的常量字符串,而 invalidConstString 是一個無效的常量字符串,因為它包含了不能在常量字符串中使用的變量和列表。

合併字串

var s1 = 'String '
'concatenation'
" works even over line breaks.";
print(s1); // String concatenation works even over line breaks.

var s2 = 'The + operator ' + 'works, as well.';
print(s2); // The + operator works, as well.

多行文本

使用 ''' 來定義多行文本:

var s1 = '''
You can create
multi-line strings like this one.
''';
print(s1);
/// You can create
/// multi-line strings like this one.

var s2 = """This is also a
multi-line string.""";
print(s2);
/// This is also a
/// multi-line string.

Raw string (原始字符串)

使用 r 前綴來創建一個原始字符串
下方例子的 \n 不會被視為換行符,而只是兩個字符 \n

var s = r'In a raw string, not even \n gets special treatment.';
print(s); // In a raw string, not even \n gets special treatment.

Runes (字符)

雖然說可以使用 Runes 定義字符型別,但官方範例介紹的以 characters API 替代寫法更簡單粗暴明瞭:

var hi = 'Hi  \u2665';
print(hi); // Hi ♥

Records (紀錄)

紀錄是匿名、不可更改的聚合型別,同俗性來說,個人覺得他跟 TypeScript 或 Python 的元組概念極為相似。
基本語法如下:

// method1
var record = ('first', a: 2, b: true, 'last');

// method2
(String, int) record;
record = ('A string', 123);

// method3
({int a, bool b}) record;
record = (a: 123, b: true);

如果需要取出 record 中的值可以使用 $<position> (但這個位置會跳過有命名的部分) 或是直接使用 .<name>

var record = ('first', a: 2, b: true, 'last');

print(record.$1); // Prints 'first'
print(record.a); // Prints 2
print(record.b); // Prints true
print(record.$2); // Prints 'last'

使用 Dart 記錄型態返回多個值並進行解構

這段說明展示了如何使用 Dart 的記錄型態來讓函數返回多個值,並使用模式匹配將這些返回值解構成局部變量。

  1. 函數返回多個值:函數 userInfo 接受一個 Map<String, dynamic> 類型的參數 json,並返回一個包含兩個值(Stringint)的記錄:
(String name, int age) userInfo(Map<String, dynamic> json) {
return (json['name'] as String, json['age'] as int);
}
  1. JSON 數據:這裡有一個 JSON 格式的 Map 對象 json,包含了鍵值對:
final json = <String, dynamic>{
'name': 'Dash',
'age': 10,
'color': 'blue',
};
  1. 解構記錄:使用模式匹配解構記錄的值並將它們賦值給局部變量 nameage
var (name, age) = userInfo(json);

這段代碼等價於:

var info = userInfo(json);
var name = info.$1;
var age = info.$2;

也就是說,首先調用 userInfo(json) 函數返回一個記錄,然後從這個記錄中提取字段 $1$2,分別賦值給變量 nameage


List

基本就是 JavaScript 的 array、Python 的 List,所以使用方面基本上相同:

var list = [1, 2, 3];

print(list.length); // 3
print(list[1]); // 2

Set

Dart 集合基本上也跟 JavaScript 跟 Python 類似,都是用來存唯一值的資料結構。

var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
info

什麼時候使用 Set 而不是 List 先來看看兩者的特性:

Set 的特性

  • 唯一性Set 保證其中的元素是唯一的,不允許重複的值。
  • 快速查找Set 提供了高效的查找操作。查找一個元素是否在 Set 中的時間複雜度為 O(1)。
  • 無序性Set 中的元素是無序的,這意味著它不會保持插入的順序。

List 的特性

  • 允許重複List 允許存儲重複的元素。
  • 有序性List 保持插入元素的順序,因此可以按索引訪問元素。
  • 操作多樣性List 提供了更多的操作和方法,例如排序、按索引訪問和切片等。

所以什麼時候使用 Set 而不是 List

  • 需要唯一性:如果你需要確保集合中的元素不重複,使用 Set
  • 需要高效查找:如果你需要頻繁檢查某個元素是否存在於集合中,使用 Set 會更高效。
  • 不關心順序:如果你不關心元素的插入順序,使用 Set

將一個集合或列表中的所有元素添加到另一個集合或列表中:

var elements = <String>{};
elements.add('fluorine');

var halogens = {'chlorine', 'bromine', 'iodine'};
elements.addAll(halogens);

print(elements); // {fluorine, chlorine, bromine, iodine}

Maps

Dart 的映射類似於 JavaScript 的 Object 和 Python 的 Dictionary。這些資料結構都是用來存儲鍵值對的集合。

var gifts = {
// Key: Value
'first': 'partridge',
'second': 'turtledoves',
'fifth': 'golden rings'
};

var nobleGases = {
2: 'helium',
10: 'neon',
18: 'argon',
};

或是可以這樣寫:

var gifts = Map<String, String>();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';

var nobleGases = Map<int, String>();
nobleGases[2] = 'helium';
nobleGases[10] = 'neon';
nobleGases[18] = 'argon';

Operators of collections

Spread operators

var list = [1, 2, 3];
var list2 = [0, ...list];
print(list2); // [0, 1, 2, 3]

如果 list 可能為 null,可以使用 ? 來幫忙程式碼執行:

var list2 = [0, ...?list];

Control-flow operators

Dart 提供了 collection ifcollection for 來在列表(List)、映射(Map)和集合(Set)的字面量中使用條件(if)和重複(for)運算符。
可以使用這些運算符來根據條件或重複操作來構建集合。

collection if

bool promoActive = true;
var nav = ['Home', 'Furniture', 'Plants', if (promoActive) 'Outlet'];
print(nav); // 輸出: [Home, Furniture, Plants, Outlet]

collection if-case

String login = 'Manager'; // 假設當前登入用戶是 Manager
var nav = ['Home', 'Furniture', 'Plants', if (login case 'Manager') 'Inventory'];
print(nav); // 輸出: [Home, Furniture, Plants, Inventory]

collection for

var listOfInts = [1, 2, 3];
var listOfStrings = ['#0', for (var i in listOfInts) '#$i'];
print(listOfStrings); // 輸出: [#0, #1, #2, #3]

Generics (泛型)

基本上前述的一些例子中就有泛型的存在,或是如下述例子:

var names = <String>['Seth', 'Kathy', 'Lars'];
var uniqueNames = <String>{'Seth', 'Kathy', 'Lars'};
var pages = <String, String>{
'index.html': 'Homepage',
'robots.txt': 'Hints for web robots',
'humans.txt': 'We are people, not machines'
};
tip

上述的泛型並不是在接受任何類型,而是確保集合中的元素類型是一致的,即都是字符串類型。
泛型的作用是在編譯時期檢查類型,從而確保程序的類型安全性。


Typedefs (型別別名)

typedef IntList = List<int>;
IntList il = [1, 2, 3];
Buy Me A Coffee