(一)Flutter學習之Dart變量和類型系統

前言

前些日子在公司嘗試着使用 Flutter 開發一些功能。要想寫好 Flutter 程序,首先 Dart 語言是基礎。前端

固然,在實際開發中,可能因爲時間的關係,不用瞭解到 Dart 的方方面面纔開始 Flutter 的開發,能夠邊學邊用。python

爲了造成一個完整的體系,我想先系統的講解下 Dart 語言,而後在開始 Flutter 相關的介紹。git

Dart 相對於 Java 來講有更強的變現力,甚至我以爲比 Kotlin 還要有表現力。Dart 不只能夠用來開發客戶端,還能夠用來開發Web前端和後端開發。github

因此呢,我以爲 Dart 仍是一門值得學習的語言,下面就開始咱們的 Dart 學習之旅吧web

本文是 Dart 相關的第一篇,主要介紹 Dart 語言的變量的定義和類型體系正則表達式

Hello World

按照慣例,學習一門語言都是從 Hello World 開始的。下面咱們看下 Dart 的 Hello World 程序:express

main(){

  var str = "Hello world!";

  print(str);
  
}
複製代碼
  • 首先咱們定義了頂級的 main 函數,這個程序的入口,若是沒有返回值能夠省略函數的返回類型
  • 而後咱們經過 var 關鍵字定義了一個字符串,經過類型推導,str是一個字符串類型的變量
  • 最後經過系統的 print 函數輸出 str 變量

變量的定義

上面咱們提到了 Dart 是支持類型推導的,因此在定義變量的時候能夠經過 var 關鍵字來定義編程

固然也能夠顯示的指定 變量的類型,如:後端

// 經過 var 關鍵字定義變量,省略具體的類型
var str = "Hello world!";

// 顯式指定變量類型
String str = "Hello World!"
複製代碼

在 Dart 中萬物皆對象,如 null、函數、數字 都是對象數組

因此咱們的定義的變量若是沒有給初始值,那麼這個變量的默認是就是 null

int lineCount;
assert(lineCount == null);
複製代碼

定義變量的時候,可使用 final 關鍵字來修飾,如

final String name = "chiclaim";

name = "johnny"; // Error

複製代碼

final定義的變量,在定義的時候就要初始化,而且只能賦值一次

定義變量的時候還可使用 const 關鍵字來修飾:

const name = "chiclaim"

name = "johnny"; // Error

複製代碼

const 關鍵字修飾變量,說明這個變量是 編譯時常量,如數字、字符串字面量都是 編譯時常量

constfinal 區別是 final 表示這個變量只能被賦值一次,const 表示該變量不只只能被賦值一次,而且該變量是一個常量

const 不只能夠修飾變量,還能夠修飾變量的值,表示該值不能修改:

main(){

  // 經過 const 關鍵字修飾foo變量的值
  var foo = const [];
  // 因爲 foo 的值被 const 修飾,因此不能修改裏面的值
  // 運行時錯誤,Unsupported operation: Cannot modify an unmodifiable list
  foo[0] = 0;
  // 能夠賦值,由於 foo 變量沒有被 final 或 const 修飾
  foo = [1,2,3];
  // 因爲foo變量被從新賦值,因此能夠修改裏面的值
  foo[0] = 0;
  
  print(foo);

}
複製代碼

Dart類型系統

Dart 類型系統以下所示:

  • numbers(數字如整型,浮點型等)
  • strings(字符串)
  • booleans (布爾值)
  • lists (集合框架和數組)
  • sets (Set集合)
  • maps (Map集合)
  • runes (表達Unicode字符)
  • symbols (通常用不到)

numbers

不像 C/C++/Java 等語言,在 Dart 表示數字的類型只有兩個:int 和 double

  • int :表示沒有小數點的整型,最大值不大於64-bit(-2^63 ~ 2^63-1)底層依賴的系統最大值也不一樣,若是編譯後用於JavaScript,則最大值爲 -2^53 ~ 2^53-1

  • double 表示雙精度的浮點型

int age = 17;

double weight = 65.5;

double waistline = 30; // 至關於 30.0

複製代碼

字符串和數字類型之間互轉:

// 字符串轉化成整型
int num = int.parse("2");

// 字符串轉化成浮點型
double pi = double.parse("3.14");

// 浮點型轉成字符串
String piStr = 3.14.toString();

// 浮點型轉成用小數表示的字符串(能夠選擇保留幾位小數)
String piAsStringFix = 3.14159.toStringAsFixed(2);

複製代碼

string 字符串

字符串的建立

一個 Dart 字符串是個 UTF-16 碼元(code units)序列,可使用單引號或雙引號來建立一個字符串

// 雙引號建立一個字符串字面量
var s1 = 'Single quotes work well for string literals.';
// 單引號建立一個字符串字面量
var s2 = "Double quotes work just as well.";
//  單引號建立字符串字面量(須要轉義符)
var s3 = 'It\'s easy to escape the string delimiter.'; var s4 = "It's even easier to use the other delimiter."; 複製代碼

建立原生(raw)字符串,在字符串前面加上前綴 r

// 通常 \n 是換行符,若是咱們想把 \n 當作字符串輸出時候,也可使用建立原生字符串的方式
var raw = r'In a raw string, not even \n gets special treatment.';
複製代碼

咱們還能夠將表達式(${expression})嵌入字符串中:

const name = "chiclaim";
// 將表達式${expression}嵌入字符串中
var info = "My name is ${name}";
// 若是表達式是一個標識符,能夠省略花括號
var info2 = "My name is $name";
複製代碼

字符串的拼接:

// 1. 經過 + 號來拼接字符串
var s2 = 'The + operator ' + 'works, as well.';

// 2. 毗鄰的字符串字面量
var s1 = 'String '
    'concatenation'
    " works even over line breaks.";

// 3. 經過多行字符串(使用三引號)
var s1 = ''' You can create multi-line strings like this one. ''';

var s2 = """This is also a multi-line string.""";

複製代碼

字符串經常使用的函數

1. 字符串查詢相關
  • contains(str) 字符串中是否包含某個字符串
  • startsWith(str) 字符串是否以某個字符串開頭
  • endsWith(str) 字符串是否以某個字符串結尾
  • indexOf(str) 字符串在目標字符串中的位置
// 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 字符串長度
  • [index] 經過索引獲取字符串中UTF-16編碼單元
  • codeUnits 返回字符串的UTF-16編碼單元的不可修改的List
// 字符串截取
assert('Never odd or even'.substring(6, 9) == 'odd');

// 使用正則表達式分割字符串,返回字符串集合
var parts = 'structured web apps'.split(' ');

// 經過索引獲取字符串的UTF-16編碼單元
assert('Never odd or even'[0] == 'N');

// 循環打印字符串中的字符串
for (var char in 'hello'.split('')) {
    print(char);
}

// 獲取字符串中的全部UTF-16碼元
var codeUnitList ='Never odd or even'.codeUnits.toList();

// N 的 ASCII 編碼的十進制就是 78
assert(codeUnitList[0] == 78);
複製代碼
3. 大小寫轉換
  • toUpperCase 將字符串所有轉成大寫
  • toLowerCase 將字符串所有轉成小寫
// 轉成大寫
assert('structured web apps'.toUpperCase() == 'STRUCTURED WEB APPS');

// 轉成小寫
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

字符串是不可變的,替換函數不是修改原字符串,而是會產生一個新的字符串

var greetingTemplate = 'Hello, NAME!';
var greeting =  greetingTemplate.replaceAll(RegExp('NAME'), 'Bob');

// greetingTemplate didn't change. assert(greeting != greetingTemplate); 複製代碼
6. 字符串構建

經過編程的方式生成一個字符串,可使用 StringBuffer 來實現,直到調用 StringBuffer.toString() 函數纔會建立字符串

var sb = StringBuffer();

// 經過 .. 實現鏈式調用
sb
  ..write('Use a StringBuffer for ')
  // 寫入一個字符串列表,以空格分割
  ..writeAll(['efficient', 'string', 'creation'], ' ')
  ..write('.');

// 建立字符串
var fullString = sb.toString();

assert(fullString ==
    'Use a StringBuffer for efficient string creation.');
複製代碼

Boolean

在 Dart 中經過 bool 關鍵字來描述布爾,布爾有兩個值:true和false。 true 和 false 都是編譯器常量

bool isSuccess = true;
bool isFailed = false;
複製代碼

List

List不只用來表示List集合(有序的數據集合),也用來表示數組

下面來看下如何建立一個List 字面量

var list = [1, 2, 3]; // 類型推導出list是 List<int>

複製代碼

介紹 const 關鍵字的時候,也用 const 修飾 List 的值 如:

var constantList = const [1, 2, 3];
// constantList[1] = 1; // Error.
複製代碼

Dart2.3 提供了展開操做符(... 和 ...?) 讓開發者更加方便的將多個值插入到集合中

var list = [1, 2, 3];
// 展開操做符(...) spread operator
var list2 = [0, ...list];


var list;
// 展開操做符(...?) null-aware spread operator
var list2 = [0, ...?list];

複製代碼

Dart2.3 除了提供了展開操做符,還有 collection ifcollection 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 在開發Flutter的時候頗有用,能夠簡化不少代碼

Set

Set 表示一個無序的集合,下面看下如何建立一個 Set 字面量

// halogens is Set<String>
var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
複製代碼

建立一個空 Set :

// 經過var和泛型推導出Set類型
var names = <String>{};

// 顯式指定是Set類型
Set<String> names = {}; 

須要注意的是下面是建立了一個Map
var names = {}; 

複製代碼

相似地,展開操做符collection if/for 也能夠用到 Set 中

Map

Map 是一個 鍵值對 集合,下面來看下如何建立 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',
};
複製代碼

須要注意的時候,Key只能出現一次,不然會被後面的替換掉

經過中括號 [] 來添加、修改、獲取Map中的元素:

var gifts = Map();
//  添加元素
gifts['first'] = 'partridge';

var gifts = {'first': 'partridge'};
// 獲取經過Key獲取Map中的元素
assert(gifts['first'] == 'partridge');

var gifts = {'first': 'partridge'};
// 修改Map中的元素
gifts['fourth'] = 'calling birds';

複製代碼

相似地,展開操做符collection if/for 也能夠用到 Set 中

關於 Dart 的 展開操做符 以及 collection if/for 更多內容能夠查看:

(二)Flutter學習之Dart展開操做符 和 Control Flow Collections

Rune

在 Dart 中,Rune類型是一個字符串的UTF-32的碼點(code points),咱們在介紹 String 類型的時候,提到了 code units

那麼在介紹 Rune 以前,咱們先來看下 code points 和 code units 是什麼?

Unicode 編碼爲世界上全部的字母、數字、符號定義了對應的數字來表示

  • 碼點:Unicode是屬於編碼字符集的範圍。它所作的事情就是將咱們須要表示的字符表中的每一個字符映射成一個數字,這個數字被稱爲相應字符的碼點(code points)
  • 碼元:就是碼點多少個字節能存儲,好比 UTF-8 碼元就是 8 bit 也就是 1 byte,UTF-16 碼元就是 2 byte

由於 Dart 字符串是 UTF-16 碼元的序列,描述 32-bit Unitcode 的字符串須要特殊的語法

一般地, Unicode使用 \uXXXX 描述碼點 ,這裏的 XXXX 是一個4位十六進制的值,例如描述心形的符號使用 \u2665 表示

若是多於或者少於 4 個十六進制,則須要加上花括號,例如笑的表情使用 \u{1f600}

String 類有幾個屬性能夠提取字符串的 rune 信息 (例如屬性 runes),codeUnitAtcodeUnit 屬性返回 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";
}
複製代碼

可是他們是兩個徹底不一樣的概念。你甚至能夠把 dynamic 不要當作一個類型看待,dynamic 顧名思義就是動態的意思,它只是告訴編譯器不要作類型檢查

dynamic name;
Object email;

main() {
  // 將 String 賦值給 name
  name = "chiclaim";
  // 打印 name 的長度
  print(name.length);
  
  // 將 int 賦值給name
  name = 1;
  
  // 編譯器並不會報錯(編譯時不作類型檢查),運行時纔會報錯
  // NoSuchMethodError: Class 'int' has no instance getter 'length'.
  print(name.length);
  
  email = "chiclaim@gmail.com";
  // 編譯器報錯,由於 Object 沒有 length 屬性
  print(email.length);
}

複製代碼

經過上面的代碼示例,相信你對 dynamic 和 Object 的區別有了比較清楚的理解

Dart 在官網的最佳實踐中建議開發者,若是你想表達意思是任何對象均可以,使用 Object 代替 dynamic

若是你容許類型推導失敗,通常推到失敗,編譯器會默認給dynamic,可是建議顯式地寫上 dynamic ,由於代碼的閱讀者不知道你是忘記了寫類型仍是:

// 建議
dynamic mergeJson(dynamic original, dynamic changes){
}

// 不建議
mergeJson(original, changes){
}

複製代碼

Dart幾個重要的概念

  • 萬物接對象:null、函數、數字
  • Dart有類型推導功能,定義變量是能夠不指定變量類型,若是不想指定類型,可使用 dynamic 類型
  • Dart支持泛型,如 List<Int>、List<dynamic>
  • Dart支持 top-level 函數,也支持成員函數和靜態函數,同時也支持函數的嵌套
  • Dart沒有 public、private、protected 關鍵字來控制訪問權限,Dart 經過下劃線來控制訪問權限,若是如下劃線開頭,則表示只能在 library 內可見

Reference

關於 Dart 變量和類型系統 就講到這裏, 更多的關於 Android 學習資料能夠查看個人GitHub: github.com/chiclaim/An…

dart.dev/guides/lang… github.com/acmerfight/…


聯繫我

下面是個人公衆號,乾貨文章不錯過,有須要的能夠關注下,有任何問題能夠聯繫我:

公衆號:  chiclaim
相關文章
相關標籤/搜索