Dart - 型別
Dart 的紀錄無限期停更,因為我離開要我寫 Flutter 的公司了,所以 Dart 就等我哪天有興趣再繼續寫吧! 最近的興趣再 C# 跟 Unity 上~ 😎
線上練習網站 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 類的超類,除了 Null | Object obj = new Object(); |
Enum | 枚舉 | Enum | 所有枚舉的超類 | enum Color { red, green, blue } |
Future | 未來 | Future | 用於非同步 | Future<int> future = Future.delayed(Duration(seconds: 1), () => 42); |
Stream | 流 | Stream | 用於非同步 | Stream<int> stream = Stream.fromIterable([1, 2, 3]); |
Iterable | 可迭代 | Iterable | 在 for-in 循環和同步生成器函數中使用 | Iterable<int> numbers = [1, 2, 3]; |
Never | 永不 | Never | 表示一個表達式永遠不會成功完成評估,多用在長拋出異常的函式 | Never neverEndingFunction() { while(true) {} } |
dynamic | 動態 | dynamic | 表示要禁用靜態檢查,通常應該使用 Object 或 Object? 取代此型別使用 | dynamic value = 10; |
void | 無 | void | 表示值永遠不會使用,通常用作返回類型 | void function() { print('Hello'); } |
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!
注意常量字符串的使用:
// 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';
在這個例子中,aConstNum
、aConstBool
和 aConstString
都是編譯時常量,因此可以在常量字符串中使用。
但是,aNum
、aBool
和 aString
不是編譯時常量,因此不能在常量字符串中使用。
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 的記錄型態來讓函數返回多個值,並使用模式匹配將這些返回值解構成局部變量。
- 函數返回多個值:函數
userInfo
接受一個Map<String, dynamic>
類型的參數json
,並返回一個包含兩個值(String
和int
)的記錄:
(String name, int age) userInfo(Map<String, dynamic> json) {
return (json['name'] as String, json['age'] as int);
}
- JSON 數據:這裡有一個 JSON 格式的
Map
對象json
,包含了鍵值對:
final json = <String, dynamic>{
'name': 'Dash',
'age': 10,
'color': 'blue',
};
- 解構記錄:使用模式匹配解構記錄的值並將它們賦值給局部變量
name
和age
:
var (name, age) = userInfo(json);
這段代碼等價於:
var info = userInfo(json);
var name = info.$1;
var age = info.$2;
也就是說,首先調用 userInfo(json)
函數返回一個記錄,然後從這個記錄中提取字段 $1
和 $2
,分別賦值給變量 name
和 age
。
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'};
什麼時候使用 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 if
和 collection 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'
};
上述的泛型並不是在接受任何類型,而是確保集合中的元素類型是一致的,即都是字符串類型。
泛型的作用是在編譯時期檢查類型,從而確保程序的類型安全性。
Typedefs (型別別名)
typedef IntList = List<int>;
IntList il = [1, 2, 3];