前言
前些日子在公司嘗試著使用 Flutter 開(kāi)發(fā)一些功能陕悬。要想寫好 Flutter 程序,首先 Dart 語(yǔ)言是基礎(chǔ)芳来。
當(dāng)然,在實(shí)際開(kāi)發(fā)中猜拾,可能由于時(shí)間的關(guān)系即舌,不用了解到 Dart 的方方面面才開(kāi)始 Flutter 的開(kāi)發(fā),可以邊學(xué)邊用关带。
為了形成一個(gè)完整的體系侥涵,我想先系統(tǒng)的講解下 Dart 語(yǔ)言沼撕,然后在開(kāi)始 Flutter 相關(guān)的介紹。
Dart 相對(duì)于 Java 來(lái)說(shuō)有更強(qiáng)的變現(xiàn)力芜飘,甚至我覺(jué)得比 Kotlin 還要有表現(xiàn)力务豺。Dart 不僅可以用來(lái)開(kāi)發(fā)客戶端,還可以用來(lái)開(kāi)發(fā)Web前端和后端開(kāi)發(fā)嗦明。
所以呢笼沥,我覺(jué)得 Dart 還是一門值得學(xué)習(xí)的語(yǔ)言,下面就開(kāi)始我們的 Dart 學(xué)習(xí)之旅吧
本文是 Dart 相關(guān)的第一篇娶牌,主要介紹 Dart 語(yǔ)言的變量的定義和類型體系
Hello World
按照慣例奔浅,學(xué)習(xí)一門語(yǔ)言都是從 Hello World 開(kāi)始的。下面我們看下 Dart 的 Hello World 程序:
main(){
var str = "Hello world!";
print(str);
}
- 首先我們定義了頂級(jí)的 main 函數(shù)诗良,這個(gè)程序的入口汹桦,如果沒(méi)有返回值可以省略函數(shù)的返回類型
- 然后我們通過(guò) var 關(guān)鍵字定義了一個(gè)字符串,通過(guò)類型推導(dǎo)鉴裹,str是一個(gè)字符串類型的變量
- 最后通過(guò)系統(tǒng)的 print 函數(shù)輸出 str 變量
變量的定義
上面我們提到了 Dart 是支持類型推導(dǎo)的舞骆,所以在定義變量的時(shí)候可以通過(guò) var 關(guān)鍵字來(lái)定義
當(dāng)然也可以顯示的指定 變量的類型,如:
// 通過(guò) var 關(guān)鍵字定義變量径荔,省略具體的類型
var str = "Hello world!";
// 顯式指定變量類型
String str = "Hello World!"
在 Dart 中萬(wàn)物皆對(duì)象督禽,如 null、函數(shù)总处、數(shù)字 都是對(duì)象
所以我們的定義的變量如果沒(méi)有給初始值狈惫,那么這個(gè)變量的默認(rèn)是就是 null
int lineCount;
assert(lineCount == null);
定義變量的時(shí)候,可以使用 final 關(guān)鍵字來(lái)修飾鹦马,如
final String name = "chiclaim";
name = "johnny"; // Error
final定義的變量胧谈,在定義的時(shí)候就要初始化,并且只能賦值一次
定義變量的時(shí)候還可以使用 const 關(guān)鍵字來(lái)修飾:
const name = "chiclaim"
name = "johnny"; // Error
const 關(guān)鍵字修飾變量菠红,說(shuō)明這個(gè)變量是 編譯時(shí)常量第岖,如數(shù)字、字符串字面量都是 編譯時(shí)常量
const 和 final 區(qū)別是 final 表示這個(gè)變量只能被賦值一次试溯,const 表示該變量不僅只能被賦值一次,并且該變量是一個(gè)常量
const 不僅可以修飾變量郊酒,還可以修飾變量的值遇绞,表示該值不能修改:
main(){
// 通過(guò) const 關(guān)鍵字修飾foo變量的值
var foo = const [];
// 由于 foo 的值被 const 修飾,所以不能修改里面的值
// 運(yùn)行時(shí)錯(cuò)誤燎窘,Unsupported operation: Cannot modify an unmodifiable list
foo[0] = 0;
// 可以賦值摹闽,因?yàn)?foo 變量沒(méi)有被 final 或 const 修飾
foo = [1,2,3];
// 由于foo變量被重新賦值,所以可以修改里面的值
foo[0] = 0;
print(foo);
}
Dart類型系統(tǒng)
Dart 類型系統(tǒng)如下所示:
- numbers(數(shù)字如整型褐健,浮點(diǎn)型等)
- strings(字符串)
- booleans (布爾值)
- lists (集合框架和數(shù)組)
- sets (Set集合)
- maps (Map集合)
- runes (表達(dá)Unicode字符)
- symbols (一般用不到)
numbers
不像 C/C++/Java 等語(yǔ)言付鹿,在 Dart 表示數(shù)字的類型只有兩個(gè):int 和 double
int :表示沒(méi)有小數(shù)點(diǎn)的整型澜汤,最大值不大于64-bit(-2^63 ~ 2^63-1)底層依賴的系統(tǒng)最大值也不同,如果編譯后用于JavaScript舵匾,則最大值為 -2^53 ~ 2^53-1
double 表示雙精度的浮點(diǎn)型
int age = 17;
double weight = 65.5;
double waistline = 30; // 相當(dāng)于 30.0
字符串和數(shù)字類型之間互轉(zhuǎn):
// 字符串轉(zhuǎn)化成整型
int num = int.parse("2");
// 字符串轉(zhuǎn)化成浮點(diǎn)型
double pi = double.parse("3.14");
// 浮點(diǎn)型轉(zhuǎn)成字符串
String piStr = 3.14.toString();
// 浮點(diǎn)型轉(zhuǎn)成用小數(shù)表示的字符串(可以選擇保留幾位小數(shù))
String piAsStringFix = 3.14159.toStringAsFixed(2);
string 字符串
字符串的創(chuàng)建
一個(gè) Dart 字符串是個(gè) UTF-16 碼元(code units)序列俊抵,可以使用單引號(hào)或雙引號(hào)來(lái)創(chuàng)建一個(gè)字符串
// 雙引號(hào)創(chuàng)建一個(gè)字符串字面量
var s1 = 'Single quotes work well for string literals.';
// 單引號(hào)創(chuàng)建一個(gè)字符串字面量
var s2 = "Double quotes work just as well.";
// 單引號(hào)創(chuàng)建字符串字面量(需要轉(zhuǎn)義符)
var s3 = 'It\'s easy to escape the string delimiter.';
var s4 = "It's even easier to use the other delimiter.";
創(chuàng)建原生(raw)字符串,在字符串前面加上前綴 r:
// 一般 \n 是換行符坐梯,如果我們想把 \n 當(dāng)做字符串輸出時(shí)候徽诲,也可以使用創(chuàng)建原生字符串的方式
var raw = r'In a raw string, not even \n gets special treatment.';
我們還可以將表達(dá)式(${expression})嵌入字符串中:
const name = "chiclaim";
// 將表達(dá)式${expression}嵌入字符串中
var info = "My name is ${name}";
// 如果表達(dá)式是一個(gè)標(biāo)識(shí)符,可以省略花括號(hào)
var info2 = "My name is $name";
字符串的拼接:
// 1. 通過(guò) + 號(hào)來(lái)拼接字符串
var s2 = 'The + operator ' + 'works, as well.';
// 2. 毗鄰的字符串字面量
var s1 = 'String '
'concatenation'
" works even over line breaks.";
// 3. 通過(guò)多行字符串(使用三引號(hào))
var s1 = '''
You can create
multi-line strings like this one.
''';
var s2 = """This is also a
multi-line string.""";
字符串常用的函數(shù)
1. 字符串查詢相關(guān)
- contains(str) 字符串中是否包含某個(gè)字符串
- startsWith(str) 字符串是否以某個(gè)字符串開(kāi)頭
- endsWith(str) 字符串是否以某個(gè)字符串結(jié)尾
- indexOf(str) 字符串在目標(biāo)字符串中的位置
// Check whether a string contains another string.
assert('Never odd or even'.contains('odd'));
// Does a string start with another string?
assert('Never odd or even'.startsWith('Never'));
// Does a string end with another string?
assert('Never odd or even'.endsWith('even'));
// Find the location of a string inside a string.
assert('Never odd or even'.indexOf('odd') == 6);
2. 字符串提取
- substring(startIndex,endIndex) 字符串截取
- split(Pattern) 字符串分割
- length 字符串長(zhǎng)度
- [index] 通過(guò)索引獲取字符串中UTF-16編碼單元
- codeUnits 返回字符串的UTF-16編碼單元的不可修改的List
// 字符串截取
assert('Never odd or even'.substring(6, 9) == 'odd');
// 使用正則表達(dá)式分割字符串吵血,返回字符串集合
var parts = 'structured web apps'.split(' ');
// 通過(guò)索引獲取字符串的UTF-16編碼單元
assert('Never odd or even'[0] == 'N');
// 循環(huán)打印字符串中的字符串
for (var char in 'hello'.split('')) {
print(char);
}
// 獲取字符串中的所有UTF-16碼元
var codeUnitList ='Never odd or even'.codeUnits.toList();
// N 的 ASCII 編碼的十進(jìn)制就是 78
assert(codeUnitList[0] == 78);
3. 大小寫轉(zhuǎn)換
- toUpperCase 將字符串全部轉(zhuǎn)成大寫
- toLowerCase 將字符串全部轉(zhuǎn)成小寫
// 轉(zhuǎn)成大寫
assert('structured web apps'.toUpperCase() == 'STRUCTURED WEB APPS');
// 轉(zhuǎn)成小寫
assert('STRUCTURED WEB APPS'.toLowerCase() == 'structured web apps');
4. 字符串的頭尾空格的移除和空字符串
- trim 去除頭和尾的空格
- isEmpty 字符串是否為空
- isNotEmpty 字符串是否不為空
// 去除頭尾的空格
print(' hello '.trim() == 'hello');
// 字符串是否為空
print(''.isEmpty);
// 空格不是空字符串
print(' '.isNotEmpty);
// 空格trim后谎替,就變成空字符串
print(' '.trim().isNotEmpty);
5. 字符串部分替換
- replaceAll
字符串是不可變的,替換函數(shù)不是修改原字符串蹋辅,而是會(huì)產(chǎn)生一個(gè)新的字符串
var greetingTemplate = 'Hello, NAME!';
var greeting = greetingTemplate.replaceAll(RegExp('NAME'), 'Bob');
// greetingTemplate didn't change.
assert(greeting != greetingTemplate);
6. 字符串構(gòu)建
通過(guò)編程的方式生成一個(gè)字符串钱贯,可以使用 StringBuffer 來(lái)實(shí)現(xiàn),直到調(diào)用 StringBuffer.toString() 函數(shù)才會(huì)創(chuàng)建字符串
var sb = StringBuffer();
// 通過(guò) .. 實(shí)現(xiàn)鏈?zhǔn)秸{(diào)用
sb
..write('Use a StringBuffer for ')
// 寫入一個(gè)字符串列表侦另,以空格分割
..writeAll(['efficient', 'string', 'creation'], ' ')
..write('.');
// 創(chuàng)建字符串
var fullString = sb.toString();
assert(fullString ==
'Use a StringBuffer for efficient string creation.');
Boolean
在 Dart 中通過(guò) bool 關(guān)鍵字來(lái)描述布爾喷舀,布爾有兩個(gè)值:true和false。 true 和 false 都是編譯器常量
bool isSuccess = true;
bool isFailed = false;
List
List不僅用來(lái)表示List集合(有序的數(shù)據(jù)集合)淋肾,也用來(lái)表示數(shù)組
下面來(lái)看下如何創(chuàng)建一個(gè)List 字面量
var list = [1, 2, 3]; // 類型推導(dǎo)出list是 List<int>
介紹 const 關(guān)鍵字的時(shí)候硫麻,也用 const 修飾 List 的值 如:
var constantList = const [1, 2, 3];
// constantList[1] = 1; // Error.
Dart2.3 提供了展開(kāi)操作符(... 和 ...?) 讓開(kāi)發(fā)者更加方便的將多個(gè)值插入到集合中
var list = [1, 2, 3];
// 展開(kāi)操作符(...) spread operator
var list2 = [0, ...list];
var list;
// 展開(kāi)操作符(...?) null-aware spread operator
var list2 = [0, ...?list];
Dart2.3 除了提供了展開(kāi)操作符,還有 collection if 和 collection for
// collection if
var nav = [
'Home',
'Furniture',
'Plants',
if (promoActive) 'Outlet'
];
// collection for
var listOfInts = [1, 2, 3];
var listOfStrings = [
'#0',
for (var i in listOfInts) '#$i'
];
assert(listOfStrings[1] == '#1');
collection if/for 在開(kāi)發(fā)Flutter的時(shí)候很有用樊卓,可以簡(jiǎn)化很多代碼
Set
Set 表示一個(gè)無(wú)序的集合拿愧,下面看下如何創(chuàng)建一個(gè) Set 字面量
// halogens is Set<String>
var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
創(chuàng)建一個(gè)空 Set :
// 通過(guò)var和泛型推導(dǎo)出Set類型
var names = <String>{};
// 顯式指定是Set類型
Set<String> names = {};
需要注意的是下面是創(chuàng)建了一個(gè)Map
var names = {};
類似地,展開(kāi)操作符 和 collection if/for 也可以用到 Set 中
Map
Map 是一個(gè) 鍵值對(duì) 集合碌尔,下面來(lái)看下如何創(chuàng)建 Map 字面量:
// gifts is Map<String, String>
var gifts = {
// Key: Value
'first': 'partridge',
'second': 'turtledoves',
'fifth': 'golden rings'
};
// nobleGases is Map<int, String>
var nobleGases = {
2: 'helium',
10: 'neon',
18: 'argon',
};
需要注意的時(shí)候浇辜,Key只能出現(xiàn)一次,否則會(huì)被后面的替換掉
通過(guò)中括號(hào) [] 來(lái)添加唾戚、修改柳洋、獲取Map中的元素:
var gifts = Map();
// 添加元素
gifts['first'] = 'partridge';
var gifts = {'first': 'partridge'};
// 獲取通過(guò)Key獲取Map中的元素
assert(gifts['first'] == 'partridge');
var gifts = {'first': 'partridge'};
// 修改Map中的元素
gifts['fourth'] = 'calling birds';
類似地,展開(kāi)操作符 和 collection if/for 也可以用到 Set 中
關(guān)于 Dart 的 展開(kāi)操作符 以及 collection if/for 更多內(nèi)容可以查看:
(二)Flutter學(xué)習(xí)之Dart展開(kāi)操作符 和 Control Flow Collections
Rune
在 Dart 中叹坦,Rune類型是一個(gè)字符串的UTF-32的碼點(diǎn)(code points)熊镣,我們?cè)诮榻B String 類型的時(shí)候,提到了 code units
那么在介紹 Rune 之前募书,我們先來(lái)看下 code points 和 code units 是什么绪囱?
Unicode 編碼為世界上所有的字母、數(shù)字莹捡、符號(hào)定義了對(duì)應(yīng)的數(shù)字來(lái)表示
- 碼點(diǎn):Unicode是屬于編碼字符集的范圍鬼吵。它所做的事情就是將我們需要表示的字符表中的每個(gè)字符映射成一個(gè)數(shù)字,這個(gè)數(shù)字被稱為相應(yīng)字符的碼點(diǎn)(code points)
- 碼元:就是碼點(diǎn)多少個(gè)字節(jié)能存儲(chǔ)篮赢,比如 UTF-8 碼元就是 8 bit 也就是 1 byte齿椅,UTF-16 碼元就是 2 byte
因?yàn)?Dart 字符串是 UTF-16 碼元的序列琉挖,描述 32-bit Unitcode 的字符串需要特殊的語(yǔ)法
通常地, Unicode使用 \uXXXX 描述碼點(diǎn) 涣脚,這里的 XXXX 是一個(gè)4位十六進(jìn)制的值示辈,例如描述心形的符號(hào)使用 \u2665 表示
如果多于或者少于 4 個(gè)十六進(jìn)制,則需要加上花括號(hào)涩澡,例如笑的表情使用 \u{1f600}
String 類有幾個(gè)屬性可以提取字符串的 rune 信息 (例如屬性 runes)顽耳,codeUnitAt 和 codeUnit 屬性返回 16 bit的 碼元(code units)
main() {
var clapping = '\u{1f44f}';
print(clapping);
print(clapping.codeUnits);
print(clapping.runes.toList());
Runes input = new Runes(
'\u2665 \u{1f605} \u{1f60e} \u{1f47b} \u{1f596} \u{1f44d}');
print(new String.fromCharCodes(input));
}
Object 和 dynamic
在 Dart 所有的類都是繼承自 Object,和Java類似妙同。dynamic 和 Object 允許所有的值賦值給它:
dynamic name;
Object email;
main() {
name = "chiclaim";
email = "chiclaim@gmail.com";
}
但是他們是兩個(gè)完全不同的概念射富。你甚至可以把 dynamic 不要當(dāng)做一個(gè)類型看待,dynamic 顧名思義就是動(dòng)態(tài)的意思粥帚,它只是告訴編譯器不要做類型檢查
dynamic name;
Object email;
main() {
// 將 String 賦值給 name
name = "chiclaim";
// 打印 name 的長(zhǎng)度
print(name.length);
// 將 int 賦值給name
name = 1;
// 編譯器并不會(huì)報(bào)錯(cuò)(編譯時(shí)不做類型檢查)胰耗,運(yùn)行時(shí)才會(huì)報(bào)錯(cuò)
// NoSuchMethodError: Class 'int' has no instance getter 'length'.
print(name.length);
email = "chiclaim@gmail.com";
// 編譯器報(bào)錯(cuò),因?yàn)?Object 沒(méi)有 length 屬性
print(email.length);
}
通過(guò)上面的代碼示例芒涡,相信你對(duì) dynamic 和 Object 的區(qū)別有了比較清楚的理解
Dart 在官網(wǎng)的最佳實(shí)踐中建議開(kāi)發(fā)者柴灯,如果你想表達(dá)意思是任何對(duì)象都可以,使用 Object 代替 dynamic
如果你允許類型推導(dǎo)失敗费尽,一般推到失敗赠群,編譯器會(huì)默認(rèn)給dynamic,但是建議顯式地寫上 dynamic 旱幼,因?yàn)榇a的閱讀者不知道你是忘記了寫類型還是:
// 建議
dynamic mergeJson(dynamic original, dynamic changes){
}
// 不建議
mergeJson(original, changes){
}
Dart幾個(gè)重要的概念
- 萬(wàn)物接對(duì)象:null查描、函數(shù)、數(shù)字
- Dart有類型推導(dǎo)功能柏卤,定義變量是可以不指定變量類型冬三,如果不想指定類型,可以使用 dynamic 類型
- Dart支持泛型缘缚,如 List<Int>勾笆、List<dynamic>
- Dart支持 top-level 函數(shù),也支持成員函數(shù)和靜態(tài)函數(shù)桥滨,同時(shí)也支持函數(shù)的嵌套
- Dart沒(méi)有 public窝爪、private、protected 關(guān)鍵字來(lái)控制訪問(wèn)權(quán)限该园,Dart 通過(guò)下劃線來(lái)控制訪問(wèn)權(quán)限酸舍,如果以下劃線開(kāi)頭,則表示只能在 library 內(nèi)可見(jiàn)
Reference
關(guān)于 Dart 變量和類型系統(tǒng) 就講到這里, 更多的關(guān)于 Android
學(xué)習(xí)資料可以查看我的GitHub: https://github.com/chiclaim/AndroidAll
https://dart.dev/guides/language/language-tour
https://github.com/acmerfight/insight_python/blob/master/Unicode_and_Character_Sets.md
更多
所有關(guān)于 Retrofit 的使用案例都在我的 AndroidAll GitHub 倉(cāng)庫(kù)中里初。該倉(cāng)庫(kù)除了 Retrofit,還有其他 Android 其他常用的開(kāi)源庫(kù)源碼分析忽舟,如「RxJava」「Glide」「LeakCanary」「Dagger2」「Retrofit」「OkHttp」「ButterKnife」「Router」等双妨。除此之外淮阐,還有完整的 Android 程序員所需要的技術(shù)棧思維導(dǎo)圖,歡迎享用刁品。
下面是我的公眾號(hào)泣特,干貨文章不錯(cuò)過(guò),有需要的可以關(guān)注下挑随,有任何問(wèn)題可以聯(lián)系我: