Dart 語言基礎入門篇

本文是【從零開始學習,開發個Flutter App】路上的第 1 篇文章。node

這篇文章介紹了 Dart 的基礎特性,目的在於讓你們創建對 Dart 語言的整體認知,初步掌握 Dart 的語法。web

咱們假定讀者已經有必定的編程基礎,若是你瞭解 JavaScript 或者 Java 等面嚮對象語言,那 Dart 學習起來應該頗有親切感。express

Dart 是一門採起衆家之長的編程語言。儘管 Dart 不少語法和 JavaScript 很類似,但 Dart 語言同時是一門強類型的語言,它同時結合了像 Java 這樣強類型面嚮對象語言的特性,這使得它能勝任大型應用開發,同時它沒有 Java 的臃腫,Dart 語言在設計上很是簡潔、靈活和高效。npm

JavaScript 從簡單的瀏覽器腳本到服務端(nodejs),慢慢延伸到PC客戶端(electron)、App (React Native)甚至小程序開發,它已然成爲一門真正意義上的全棧開發語言。編程

若是說 JavaScript 是在漫長的時光裏野蠻生長,那 Dart 從一開始就是精心設計出來的。若是說有一門語言取代JavaScript的位置,那極可能就是Dart。小程序

Talk is cheep,下面就讓咱們來親自感覺一下這門語言的吧。設計模式

變量

你能夠像 JavaScript 那樣聲明一個變量:數組

var name = 'Bob';
複製代碼

編譯器會推導出 name 的類型是String 類型,等價於:瀏覽器

String name = 'Bob';
複製代碼

咱們能夠從下面代碼窺見 Dart 是強類型語言的特性:緩存

var name = 'Bob';
  
// 調用 String 的方法
print(name.toLowerCase());

// 編譯錯誤
// name = 1;
複製代碼

前面咱們說過,Dart 除了具有簡潔的特色,並且也能夠是很是靈活的,若是你想變換一個變量的類型,你也可使用dynamic 來聲明變量,這就跟 JavaScript 同樣了:

dynamic name = 'Bob'; //String 類型
name = 1;// int 類型
print(name);
複製代碼

上面的代碼能夠正常編譯和運行,但除非你有足夠的理由,請不要輕易使用。

final 的語義和 Java 的同樣,表示該變量是不可變的:

// String 能夠省略
final String name = 'Bob'; 

// 編譯錯誤
// name = 'Mary';
複製代碼

其中 String 能夠省略,Dart 編譯器足夠聰明地知道變量name 的類型。

若是要聲明常量,可使用const 關鍵詞:

const PI = '3.14';

class Person{
  static const name = 'KK';
}

複製代碼

若是類變量,則須要聲明爲static const

內置類型

不像Java把類型分的特別細,好比整數類型,就有byteshortintlong 。Dart 的類型設計至關簡潔,這也是 Dart 容易上手的緣由之一,能夠理解爲經過犧牲空間來換取效率吧。

數值類型

Dart 內置支持兩種數值類型,分別是intdouble ,它們的大小都是64位。

var x = 1;
// 0x開頭爲16進制整數
var hex = 0xDEADBEEF;


var y = 1.1;
// 指數形式
var exponents = 1.42e5;
複製代碼

須要注意的是,在Dart中,全部變量值都是一個對象,intdouble類型也不例外,它們都是num類型的子類,這點和JavaJavaScript都不太同樣:

// String -> int
var one = int.parse('1');
assert(one == 1);

// String -> double
var onePointOne = double.parse('1.1');
assert(onePointOne == 1.1);

// int -> String
String oneAsString = 1.toString();
assert(oneAsString == '1');

// double -> String
String piAsString = 3.14159.toStringAsFixed(2);
assert(piAsString == '3.14');
複製代碼

字符串

Dart 字符串使用的是UTF-16編碼。

var s = '中';
s.codeUnits.forEach((ch) => print(ch));
// 輸出爲UNICODE值
20013
複製代碼

Dart 採用了 JavaScript 中相似模板字符串的概念,能夠在字符串經過${expression}語法插入變量:

var s = "hello";
  
print('${s}, world!');
  
//能夠簡化成:
print('$s, world!');

//調用方法
print('${s.toUpperCase()}, world!');
複製代碼

Dart 能夠直接經過==來比較字符串:

var s1 = "hello";
var s2 = "HELLO";
assert(s1.toUpperCase() == s2);
複製代碼

布爾類型

Dart 布爾類型對應爲bool關鍵詞,它有truefalse兩個值,這點和其餘語言區別不大。值得一提的是,在Dart的條件語句ifassert表達式裏面,它們的值必須是bool類型,這點和 JavaScript 不一樣。

var s = '';
assert(s.isEmpty);

if(s.isNotEmpty){
// do something
}
  
//編譯錯誤,在JavaScript經常使用來判斷undefined
if(s){
}
複製代碼

Lists

你能夠把Dart中的List對應到 JavaScript 的數組或者 Java 中的ArrayList,但 Dart 的設計更爲精巧。

你能夠經過相似 JavaScript 同樣聲明一個數組對象:

var list = [];
list.add('Hello');
list.add(1);
複製代碼

這裏List容器接受的類型是dynamic,你能夠往裏面添加任何類型的對象,但若是像這樣聲明:

var iList = [1,2,3];
iList.add(4);
//編譯錯誤 The argument type 'String' can't be assigned to the parameter type 'int'
//iList.add('Hello');
複製代碼

那麼Dart就會推導出這個List是個List<int>,今後這個List就只能接受int類型數據了,你也能夠顯式聲明List的類型:

var sList = List<String>();

//在Flutter類庫中,有許多這樣的變量聲明:
List<Widget> children = const <Widget>[];
複製代碼

上面右邊那個 const 的意思表示常量數組,在這裏你能夠理解爲一個給children賦值了一個編譯期常量空數組,這樣的作法能夠很好的節省內存,下面的例子可讓你們更好的理解常量數組的概念:

var constList = const <int>[1,2];
constList[0] = 2; //編譯經過, 運行錯誤
constList.add(3); //編譯經過, 運行錯誤
複製代碼

Dart2.3 增長了擴展運算符 (spread operator) ......?,經過下面的例子你很容易就明白它們的用法:

var list = [1, 2, 3];
var list2 = [0, ...list];
assert(list2.length == 4);
複製代碼

若是擴展對象多是null,可使用...?

var list;
 var list2 = [0, ...?list];
 assert(list2.length == 1);
複製代碼

你能夠直接在元素內進行判斷,決定是否須要某個元素:

var promoActive = true;
var nav = [
    'Home',
    'Furniture',
    'Plants',
    promoActive? 'About':'Outlet'
];
複製代碼

甚至使用for來動態添加多個元素:

var listOfInts = [1, 2, 3];
var listOfStrings = [
  '#0',
  for (var i in listOfInts) '#$i'
];
assert(listOfStrings[1] == '#1');
複製代碼

這種動態的能力使得 Flutter 在構建 Widget 樹的時候很是方便。

Sets

Set的語意和其餘語言的是同樣的,都是表示在容器中對象惟一。在Dart中,Set默認是LinkedHashSet實現,表示元素按添加前後順序排序。

聲明Set對象:

var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
複製代碼

遍歷Set,遍歷除了上面提到的for...in,你還可使用相似 Java 的 lambada 中的 forEach 形式:

halogens.add('bromine');
halogens.add('astatine');
halogens.forEach((el) => print(el));
複製代碼

輸出結果:

fluorine
chlorine
bromine
iodine
astatine
複製代碼

除了容器的對象惟一特性以外,其餘基本和List是差很少的。

// 添加類型聲明:
var elements = <String>{};

var promoActive = true;
// 動態添加元素
final navSet = {'Home', 'Furniture', promoActive? 'About':'Outlet'};
複製代碼

Maps

Map對象的聲明方式保持了 JavaScript 的習慣,Dart 中Map的默認實現是LinkedHashMap,表示元素按添加前後順序排序。

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

assert(gifts['first'] == 'partridge');
複製代碼

添加一個鍵值對:

gifts['fourth'] = 'calling birds';
複製代碼

遍歷Map

gifts.forEach((key,value) => print('key: $key, value: $value'));
複製代碼

函數

在 Dart 中,函數自己也是個對象,它對應的類型是Function,這意味着函數能夠當作變量的值或者做爲一個方法入傳參數值。

void sayHello(var name){
  print('hello, $name');
}

void callHello(Function func, var name){
  func(name);
}

void main(){
  // 函數變量
  var helloFuc = sayHello;
  // 調用函數
  helloFuc('Girl');
  // 函數參數
  callHello(helloFuc,'Boy');
}
複製代碼

輸出:

hello, Girl
hello, Boy
複製代碼

對於只有一個表達式的簡單函數,你還能夠經過=>讓函數變得更加簡潔,=> expr在這裏至關於{ return expr; } ,咱們來看一下下面的語句:

String hello(var name ) => 'hello, $name';
複製代碼

至關於:

String hello(var name ){
  return 'hello, $name';
}
複製代碼

參數

在Flutter UI庫裏面,命名參數隨處可見,下面是一個使用了命名參數(Named parameters)的例子:

void enableFlags({bool bold, bool hidden}) {...}
複製代碼

調用這個函數:

enableFlags(bold: false);
enableFlags(hidden: false);
enableFlags(bold: true, hidden: false);
複製代碼

命名參數默認是可選的,若是你須要表達該參數必傳,可使用@required

void enableFlags({bool bold, @required bool hidden}) {}
複製代碼

固然,Dart 對於通常的函數形式也是支持的:

void enableFlags(bool bold, bool hidden) {}
複製代碼

和命名參數不同,這種形式的函數的參數默認是都是要傳的:

enableFlags(false, true);
複製代碼

你可使用[]來增長非必填參數:

void enableFlags(bool bold, bool hidden, [bool option]) {}
複製代碼

另外,Dart 的函數還支持設置參數默認值:

void enableFlags({bool bold = false, bool hidden = false}) {...}

String say(String from, [String device = 'carrier pigeon', String mood]) {}
複製代碼

匿名函數

顧名思意,匿名函數的意思就是指沒有定義函數名的函數。你應該對此不陌生了,咱們在遍歷ListMap的時候已經使用過了,經過匿名函數能夠進一步精簡代碼:

var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
  print('${list.indexOf(item)}: $item');
});
複製代碼

閉包

Dart支持閉包。沒有接觸過JavaScript的同窗可能對閉包(closure)比較陌生,這裏給你們簡單解釋一下閉包。

閉包的定義比較拗口,咱們不去糾結它的具體定義,而是打算經過一個具體的例子去理解它:

Function closureFunc() {
  var name = "Flutter"; // name 是一個被 init 建立的局部變量
  void displayName() { // displayName() 是內部函數,一個閉包
    print(name); // 使用了父函數中聲明的變量
  }
  return displayName;
}

void main(){
  //myFunc是一個displayName函數
  var myFunc = closureFunc(); //(1)
  
  // 執行displayName函數
  myFunc(); // (2)
}
複製代碼

結果如咱們所料的那樣打印了Flutter

在(1)執行完以後,name做爲一個函數的局部變量,引用的對象不是應該被回收掉了嗎?可是當咱們在內函數調用外部的name時,它依然能夠神奇地被調用,這是爲何呢?

這是由於Dart在運行內部函數時會造成閉包,閉包是由函數以及建立該函數的詞法環境組合而成,這個環境包含了這個閉包建立時所能訪問的全部局部變量

咱們簡單變一下代碼:

Function closureFunc() {
  var name = "Flutter"; // name 是一個被 init 建立的局部變量
  void displayName() { // displayName() 是內部函數,一個閉包
    print(name); // 使用了父函數中聲明的變量
  }
  name = 'Dart'; //從新賦值
  return displayName;
}
複製代碼

結果輸出是Dart,能夠看到內部函數訪問外部函數的變量時,是在同一個詞法環境中的。

返回值

在Dart中,全部的函數都必須有返回值,若是沒有的話,那將自動返回null

foo() {}

assert(foo() == null);
複製代碼

流程控制

這部分和大部分語言都同樣,在這裏簡單過一下就行。

if-else

if(hasHause && hasCar){
    marry();
}else if(isHandsome){
    date();
}else{
    pass();
}
複製代碼

循環

各類for:

var list = [1,2,3];

for(var i = 0; i != list.length; i++){}

for(var i in list){}
複製代碼

while和循環中斷(中斷也是在for中適用的):

var i = 0;
  while(i != list.length){
    if(i % 2 == 0){
       continue;
    }
     print(list[i]);
  }

  i = 0;
  do{
    print(list[i]);
    if(i == 5){
      break;
    }
  }while(i != list.length);
複製代碼

若是對象是Iterable類型,你還能夠像Java的 lambada 表達式同樣:

list.forEach((i) => print(i));
  
list.where((i) =>i % 2 == 0).forEach((i) => print(i));
複製代碼

switch

switch能夠用於intdoubleStringenum等類型,switch 只能在同類型對象中進行比較,進行比較的類不要覆蓋==運算符。

var color = '';
  switch(color){
    case "RED":
      break;
    case "BLUE":
      break;
    default:
      
  }
複製代碼

assert

在Dart中,assert語句常常用來檢查參數,它的完整表示是:assert(condition, optionalMessage),若是conditionfalse,那麼將會拋出[AssertionError]異常,中止執行程序。

assert(text != null);

assert(urlString.startsWith('https'), 'URL ($urlString) should start with "https".');
複製代碼

assert 一般只用於開發階段,它在產品運行環境中一般會被忽略。在下面的場景中會打開assert

  1. Flutter 的 debug mode
  2. 一些開發工具好比dartdevc默認會開啓。
  3. 一些工具,像dartdart2js ,能夠經過參數--enable-asserts 開啓。

異常處理

Dart 的異常處理和Java很像,可是Dart中全部的異常都是非檢查型異常(unchecked exception),也就是說,你沒必要像 Java 同樣,被強制須要處理異常。

Dart 提供了ExceptionError 兩種類型的異常。 通常狀況下,你不該該對Error類型錯誤進行捕獲處理,而是儘可能避免出現這類錯誤。

好比OutOfMemoryErrorStackOverflowErrorNoSuchMethodError等都屬於Error類型錯誤。

前面提到,由於 Dart 不像 Java 那樣能夠聲明編譯期異常,這種作法可讓代碼變得更簡潔,可是容易忽略掉異常的處理,因此咱們在編碼的時候,在可能會有異常的地方要注意閱讀API文檔,另外本身寫的方法,若是有異常拋出,要在註釋處進行聲明。好比類庫中的File類其中一個方法註釋:

/** * Synchronously read the entire file contents as a list of bytes. * * Throws a [FileSystemException] if the operation fails. */
  Uint8List readAsBytesSync();
複製代碼

拋出異常

throw FormatException('Expected at least 1 section');
複製代碼

throw除了能夠拋出異常對象,它還能夠拋出任意類型對象,但建議仍是使用標準的異常類做爲最佳實踐。

throw 'Out of llamas!';
複製代碼

捕獲異常

能夠經過on 關鍵詞來指定異常類型:

var file = File("1.txt");
  try{
    file.readAsStringSync();
  } on FileSystemException {
     //do something
  }
複製代碼

使用catch關鍵詞獲取異常對象,catch有兩個參數,第一個是異常對象,第二個是錯誤堆棧。

try{
    file.readAsStringSync();
} on FileSystemException catch (e){
    print('exception: $e');
} catch(e, s){ //其他類型
   print('Exception details:\n $e');
   print('Stack trace:\n $s');
}
複製代碼

使用rethrow 拋給上一級處理:

try{
    file.readAsStringSync();
  } on FileSystemException catch (e){
    print('exception: $e');
  } catch(e){
     rethrow;
  }
複製代碼

finally

finally通常用於釋放資源等一些操做,它表示最後必定會執行的意思,即使try...catch中有return,它裏面的代碼也會承諾執行。

try{
     print('hello');
     return;
  } catch(e){
     rethrow;
  } finally{
     print('finally');
}
複製代碼

輸出:

hello
finally
複製代碼

Dart 是一門面向對象的編程語言,全部對象都是某個類的實例,全部類繼承了Object類。

一個簡單的類:

class Point {
  num x, y;

  // 構造器
  Point(this.x, this.y);
  
  // 實例方法
  num distanceTo(Point other) {
    var dx = x - other.x;
    var dy = y - other.y;
    return sqrt(dx * dx + dy * dy);
  }
}
複製代碼

類成員

Dart 經過. 來調用類成員變量和方法的。

//建立對象,new 關鍵字能夠省略
var p = Point(2, 2);

// Set the value of the instance variable y.
p.y = 3;

// Get the value of y.
assert(p.y == 3);

// Invoke distanceTo() on p.
num distance = p.distanceTo(Point(4, 4));
複製代碼

你還能夠經過.?來避免null對象。在Java 裏面,常常須要大量的空判斷來避免NullPonterException,這是讓人詬病Java的其中一個地方。而在Dart中,能夠很方便地避免這個問題:

// If p is non-null, set its y value to 4.
p?.y = 4;
複製代碼

在 Dart 中,沒有privateprotectedpublic這些關鍵詞,若是要聲明一個變量是私有的,則在變量名前添加下劃線_,聲明瞭私有的變量,只在本類庫中可見。

class Point{
  num _x;
  num _y;
}
複製代碼

構造器(Constructor)

若是沒有聲明構造器,Dart 會給類生成一個默認的無參構造器,聲明一個帶參數的構造器,你能夠像 Java這樣:

class Person{
  String name;
  int sex;
  
  Person(String name, int sex){
    this.name = name;
    this.sex = sex;
  }
}
複製代碼

也可使用簡化版:

Person(this.name, this.sex);
複製代碼

或者命名式構造器:

Person.badGirl(){
    this.name = 'Bad Girl';
    this.sex = 1;
}
複製代碼

你還能夠經過factory關鍵詞來建立實例:

Person.goodGirl(){
	this.name = 'good Girl';
	this.sex = 1;
}

factory Person(int type){
	return type == 1 ? Person.badGirl(): Person.goodGirl();
}
複製代碼

factory對應到設計模式中工廠模式的語言級實現,在 Flutter 的類庫中有大量的應用,好比Map

// 部分代碼
abstract class Map<K, V> {  
	factory Map.from(Map other) = LinkedHashMap<K, V>.from;
}
複製代碼

若是一個對象的建立過程比較複雜,好比須要選擇不一樣的子類實現或則須要緩存實例等,你就能夠考慮經過這種方法。在上面Map例子中,經過聲明 factory來選擇了建立子類LinkedHashMapLinkedHashMap.from也是一個factory,裏面是具體的建立過程)。

若是你想在對象建立以前的時候還想作點什麼,好比參數校驗,你能夠經過下面的方法:

Person(this.name, this.sex): assert(sex == 1)
複製代碼

在構造器後面添加的一些簡單操做叫作initializer list

在Dart中,初始化的順序以下:

  1. 執行initializer list;
  2. 執行父類的構造器;
  3. 執行子類的構造器。
class Person{
  String name;
  int sex;

  Person(this.sex): name = 'a', assert(sex == 1){
    this.name = 'b';
    print('Person');
  }

}

class Man extends Person{
   Man(): super(1){
     this.name = 'c';
     print('Man');
   }
}

void main(){
  Person person = Man();
  print('name : ${person.name}');
}
複製代碼

上面的代碼輸出爲:

Person
Man
name : c
複製代碼

若是子類構造器沒有顯式調用父類構造器,那麼默認會調用父類的默認無參構造器。顯式調用父類的構造器:

Man(height): this.height = height, super(1);
複製代碼

重定向構造器:

Man(this.height, this.age): assert(height > 0), assert(age > 0);

Man.old(): this(12, 60); //調用上面的構造器
複製代碼

Getter 和 Setter

在 Dart 中,對 GetterSetter 方法有專門的優化。即使沒有聲明,每一個類變量也會默認有一個get方法,在隱含接口章節會有體現。

class Rectangle {
  num left, top, width, height;

  Rectangle(this.left, this.top, this.width, this.height);

  num get right => left + width;
  set right(num value) => left = value - width;
  num get bottom => top + height;
  set bottom(num value) => top = value - height;
}

void main() {
  var rect = Rectangle(3, 4, 20, 15);
  assert(rect.left == 3);
  rect.right = 12;
  assert(rect.left == -8);
}
複製代碼

抽象類

Dart 的抽象類和Java差很少,除了不能夠實例化,能夠聲明抽象方法以外,和通常類沒有區別。

abstract class AbstractContainer {
  num _width;

  void updateChildren(); // 抽象方法,強制繼承子類實現該方法。

  get width => this._width;
  
  int sqrt(){
    return _width * _width;
  }
}
複製代碼

隱含接口

Dart 中的每一個類都隱含了定義了一個接口,這個接口包含了這個類的全部成員變量和方法,你能夠經過implements關鍵詞來從新實現相關的接口方法:

class Person {
  //隱含了 get 方法
  final _name;

  Person(this._name);

  String greet(String who) => 'Hello, $who. I am $_name.';
}

class Impostor implements Person {
  // 須要從新實現
  get _name => '';

  // 須要從新實現
  String greet(String who) => 'Hi $who. Do you know who I am?';
}
複製代碼

實現多個接口:

class Point implements Comparable, Location {...}
複製代碼

繼承

和Java基本一致,繼承使用extends關鍵詞:

class Television {
  void turnOn() {
    doSomthing();
  }
}

class SmartTelevision extends Television {

  @override
  void turnOn() {
    super.turnOn(); //調用父類方法
    doMore();
  }
}
複製代碼

重載操做符

比較特別的是,Dart 還容許重載操做符,好比List類支持的下標訪問元素,就定義了相關的接口:

E operator [](int index);
複製代碼

咱們經過下面的實例來進一步說明重載操做符:

class MyList{

  var list = [1,2,3];

  operator [](int index){
    return list[index];
  }
}

void main() {
  var list = MyList();
  print(list[1]); //輸出 2
}
複製代碼

擴展方法

這個特性也是Dart讓人眼前一亮的地方(Dart2.7以後才支持),能夠對標到 JavaScript 中的 prototype。經過這個特性,你甚至能夠給類庫添加新的方法:

//經過關鍵詞 extension 給 String 類添加新方法
extension NumberParsing on String {
  int parseInt() {
    return int.parse(this);
  }
}
複製代碼

後面String對象就能夠調用該方法了:

print('42'.parseInt()); 
複製代碼

枚舉類型

枚舉類型和保持和Java的關鍵詞一致:

enum Color { red, green, blue }
複製代碼

switch中使用:

// color 是 enmu Color 類型
switch(color){
    case Color.red:
      break;
    case Color.blue:
      break;
    case Color.green:
      break;
    default:
      break;
}
複製代碼

枚舉類型還有一個index的getter,它是個連續的數字序列,從0開始:

assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);
複製代碼

新特性:Mixins

這個特性進一步加強了代碼複用的能力,若是你有寫過Android的佈局XML代碼或者Freemaker模板的話,那這個特性就能夠理解爲其中inlclude 的功能。

聲明一個mixin類:

mixin Musical {
  bool canPlayPiano = false;
  bool canCompose = false;
  bool canConduct = false;

  void entertainMe() {
    if (canPlayPiano) {
      print('Playing piano');
    } else if (canConduct) {
      print('Waving hands');
    } else {
      print('Humming to self');
    }
  }
}
複製代碼

經過with關鍵詞進行復用:

class Musician extends Performer with Musical {
  // ···
}

class Maestro extends Person with Musical, Aggressive, Demented {
  Maestro(String maestroName) {
    name = maestroName;
    canConduct = true;
  }
}
複製代碼

mixin類甚至能夠經過on關鍵詞實現繼承的功能:

mixin MusicalPerformer on Musician {
  // ···
}
複製代碼

類變量和類方法

class Queue {
  //類變量
  static int maxLength = 1000;
  // 類常量
  static const initialCapacity = 16;
  // 類方法
  static void modifyMax(int max){
    _maxLength = max;
  }
}

void main() {
  print(Queue.initialCapacity);
  Queue.modifyMax(2);
  print(Queue._maxLength);
}
複製代碼

泛型

在面向對象的語言中,泛型主要的做用有兩點:

一、類型安全檢查,把錯誤扼殺在編譯期:

var names = List<String>();
names.addAll(['Seth', 'Kathy', 'Lars']);
//編譯錯誤
names.add(42); 
複製代碼

二、加強代碼複用,好比下面的代碼:

abstract class ObjectCache {
  Object getByKey(String key);
  void setByKey(String key, Object value);
}

abstract class StringCache {
  String getByKey(String key);
  void setByKey(String key, String value);
}
複製代碼

你能夠經過泛型把它們合併成一個類:

abstract class Cache<T> {
  T getByKey(String key);
  void setByKey(String key, T value);
}
複製代碼

在Java中,泛型是經過類型擦除來實現的,但在Dart中實打實的泛型:

var names = <String>[];
 names.addAll(['Tom',"Cat"]);
 // is 能夠用於類型判斷
 print(names is List<String>); // true
 print(names is List); // true
 print(names is List<int>); //false
複製代碼

你能夠經過extends關鍵詞來限制泛型類型,這點和Java同樣:

abstract class Animal{}
class Cat extends Animal{}
class Ext<T extends Animal>{
  T data;
}

void main() {
  var e = Ext(); // ok
  var e1 = Ext<Animal>(); // ok
  var e2 = Ext<Cat>(); // ok
  var e3 = Ext<int>(); // compile error
}
複製代碼

使用類庫

有生命力的編程語言,它背後都有一個強大的類庫,它們可讓咱們站在巨人的肩膀上,又免於從新造輪子。

導入類庫

在Dart裏面,經過import關鍵詞來導入類庫。

內置的類庫使用dart:開頭引入:

import 'dart:io';
複製代碼

瞭解更多內置的類庫能夠查看這裏

第三方類庫或者本地的dart文件用package:開頭:

好比導入用於網絡請求的dio庫:

import 'package:dio/dio.dart';
複製代碼

Dart 應用自己就是一個庫,好比個人應用名是ccsys,導入其餘文件夾的類:

import 'package:ccsys/common/net_utils.dart';
import 'package:ccsys/model/user.dart';
複製代碼

若是你使用IDE來開發,通常這個事情不用你來操心,它會自動幫你導入的。

Dart 經過pub.dev來管理類庫,相似Java世界的Maven 或者Node.js的npm同樣,你能夠在裏面找到很是多實用的庫。

解決類名衝突

若是導入的類庫有類名衝突,能夠經過as使用別名來避免這個問題:

import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;

// 使用來自 lib1 的 Element
Element element1 = Element();

// 使用來自 lib2 的 Element
lib2.Element element2 = lib2.Element();

複製代碼

導入部分類

在一個dart文件中,可能會存在不少個類,若是你只想引用其中幾個,你能夠增長show或者hide來處理:

//文件:my_lib.dart
class One {}

class Two{}

class Three{}
複製代碼

使用show導入OneTwo類:

//文件:test.dart
import 'my_lib.dart' show One, Two;

void main() {
  var one = One();
  var two = Two();
  //compile error
  var three = Three();
}
複製代碼

也可使用hide排除Three,和上面是等價的:

//文件:test.dart
import 'my_lib.dart' hide Three;

void main() {
  var one = One();
  var two = Two();
}
複製代碼

延遲加載庫

目前只有在web app(dart2js)中才支持延遲加載,Flutter、Dart VM是不支持的,咱們這裏僅作一下簡單介紹。

你須要經過deferred as來聲明延遲加載該類庫:

import 'package:greetings/hello.dart' deferred as hello;
複製代碼

當你須要使用的時候,經過loadLibrary()加載:

Future greet() async {
  await hello.loadLibrary();
  hello.printGreeting();
}
複製代碼

你能夠屢次調用loadLibrary,它不會被重複加載。

異步支持

這個話題稍微複雜,咱們將用另一篇文章獨立討論這個問題,請留意下一篇內容。

參考資料

  1. 學習Dart的十大理由

  2. A tour of the Dart language

  3. Dart 常量構造器的理解

  4. JavaScript 閉包

關於AgileStudio

咱們是一支由資深獨立開發者和設計師組成的團隊,成員均有紮實的技術實力和多年的產品設計開發經驗,提供可信賴的軟件定製服務。

相關文章
相關標籤/搜索