Flutter學習之Dart語言基礎(1)

Flutter日漸火爆,因此在進行Flutter學習前先學習一些其所使用的開發語言dart的基礎,這篇文章主要學習了

  1. Dart的基礎代碼示例
  2. Dart的概念
  3. Dart的關鍵字
Dart的基礎代碼示例
// 定義一個方法。
printInteger(int aNumber) {
  print('The number is $aNumber.'); // Print to console.
}

// main入口函數。
main() {
  var number = 42; // 聲明並初始化變量。
  printInteger(number); // 函數調用。
}
// 註釋

dart註釋方法,更多註釋可以看我的另一篇文章https://www.jianshu.com/p/d1dae0d5c472

int

數據類型,更多數據類型可看https://www.dartlang.org/guides/language/language-tour#built-in-types

print()

一種方便的顯示輸出方式

'...' (or "...")

字符串,dart中更推薦使用'...'

$variableName (or ${expression})

字符串插值:包括字符串文字內部的變量或表達式的字符串

var

一種聲明變量而不指定其類型的方法,關鍵字之一

Dart重要概念

當要學習dart時,請記住以下事實和概念:

  • 一切皆爲對象,放在變量中的所有內容都是一個對象,每個對象都是一個class的實例,numbers,函數和null都是對象,所有對象都繼承自Object類。下面給個圖你看一下,沒錯,連int都是對象:


  • 儘管Dart是強類型的,但類型註釋是可選的,因爲Dart可以推斷類型。在上面的代碼中,數字被推斷爲int類型。如果要明確說明不需要任何類型,請使用特殊類型dynamic。

  • Dart支持泛型類型,如List<int>(整數列表)或List<dynamic>(任何類型的對象列表)。

  • Dart支持頂級函數(例如main()),以及綁定到類或對象的函數(分別是靜態和實例方法),還可以在函數內創建函數(嵌套函數或本地函數)。

  • Dart也支持頂級變量,以及綁定到類或對象的變量(靜態和實例變量),實例變量有時稱爲字段或屬性。

  • 與Java不同,Dart沒有關鍵字public,protected和private,如果標識符以下劃線(_)開頭,則它代表是私有的,否則都爲公有。

  • 標識符可以以字母或下劃線(_)開頭,後跟這些字符加數字的任意組合。

  • Dart有表達式(具有運行時值)和語句(不具有運行時值)。例如,條件表達式"條件? expr1:expr2的值爲expr1或expr2"。將其與if-else語句進行比較,該語句沒有任何值。語句通常包含一個或多個表達式,但表達式不能直接包含語句。

關鍵字

dart共有60個關鍵字,所以以下篇幅可能有點長

abstract2 dynamic2 implements2 show1
as2 else import2 static2
assert enum in super
async1 export2 in2 super
await3 extends is sync1
break external2 library2 this
case factory2 mixin2 throw
catch false new true
class final null try
const finally on1 typedef2
continue for operator2 var
covariant2 Function2 part2 void
default get2 rethrow while
deferred2 hide1 return with
do if set2 yield3
  • 帶有上標1的關鍵字是上下文關鍵字,僅在特定位置具有含義。
  • 帶有上標2的關鍵字是內置標識符, 爲了簡化將JavaScript代碼移植到Dart的任務,這些關鍵字在大多數地方都是有效的標識符,但它們不能用作類或類型名稱,也不能用作導入前綴。
  • 帶有上標3的關鍵字爲新版本中的新標識符,是與Dart 1.0發佈後添加的異步支持相關的有限保留字。

詳細可看下面說明

abstract

使用abstract修飾符定義抽象類即無法實例化的類,抽象類可以自定義一些接口。抽象類通常有抽象方法,下面是一個聲明具有抽象方法的抽象類的示例:

// 該類聲明爲抽象類且不可實例化。
abstract class AbstractContainer {
  // 定義構造函數,變量,方法等...

  // 其他....  

  // 抽象方法。
  void updateChildren(); 
}

下面爲實現抽象方法的例子:

//抽象類
abstract class Doer {
  void doSomething(); // 定義一個抽象方法
}

//繼承抽象類實現抽象方法
class EffectiveDoer extends Doer {
  void doSomething() {
    // 實現邏輯
  }
}
dynamic

顧名思義,dynamic(動態), 直接先上代碼

void judge(dynamic arg){
    if (arg is bool){
      print('arg is bool');
    } else if (arg is String){
      print('arg is String');
    } else if (arg is int){
      print('arg is int');
    } else {
      print('arg is others');
    }
}

dynamic同等於Object, 即上面代碼可以等同於下面代碼:

void judge(Object arg){
    if (arg is bool){
      print('arg is bool');
    } else if (arg is String){
      print('arg is String');
    } else if (arg is int){
      print('arg is int');
    } else {
      print('arg is others');
    }
}

在Dart中,dynamic和Object可表示所有類型, 這兩者區別是使用dynamic可以處理更復雜的不確定類型,例如超出了Dart的類型系統,或者值來自互操作或者在靜態類型系統範圍之外的情況。

implements

Java中,該關鍵字用於實現接口類(interface), Dart中亦有相同的意思,實現接口,我們先看代碼:

// Person類,包含方法greet().
class Person {
  //在該類中,屬於私有,僅對當前類可見
  final _name;

  // 不是接口,只是構造函數
  Person(this._name);

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

// 實現Person類接口的類
class Impostor implements Person {
  //只是一個普通的get方法,可忽略
  get _name => '';

  //實現Person的greet方法
  String greet(String who) => 'Hi $who. Do you know who I am?';
}

//只是一個測試方法
String greetBob(Person person) => person.greet('Bob');

void main() {
  print(greetBob(Person('Kathy')));  //打印 -> Hello, Bob. I am Kathy.
  print(greetBob(Impostor())); //打印 -> Hi Bob. Do you know who I am?
}

Dart中沒有Java的interface功能,如果Impostor在不繼承Person類的情況想實現Person類的接口的話,可以使用implements關鍵字。implements可同時實現多個類的接口:

class Point implements Comparable, Location {...}
show & hide

有時候我們導入一個庫,如果只想使用庫的一部分,則可以有選擇地導入庫,例如:

// 只導入foo
import 'package:lib1/lib1.dart' show foo;
//導入整個庫除了foo
import 'package:lib2/lib2.dart' hide foo;
as, is, is!

as,is,和 !is 運算符在運行時檢查類型很方便

  • as: 類型轉換, 也用於指定庫前綴
  • is: 類似於java的instanceof
  • !is: is操作符的取反, 即不是xxx

代碼示例:

if (emp is Person) {
  // 類型檢查
  emp.firstName = 'Bob';
}
// 如果emp爲Person,則將firstName改爲Bod, 否則會在運行時期報錯
(emp as Person).firstName = 'Bob';

如果導入兩個具有衝突標識符(class)的庫,則可以爲一個或兩個庫指定前綴。 例如,如果library1和library2都有一個Element類,那麼as可以這樣使用:

import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2; //指定庫的前綴爲lib2

// Uses Element from lib1.
Element element1 = Element();

// Uses Element from lib2.
lib2.Element element2 = lib2.Element();
if & else

與Java或其他語言一樣,Dart支持帶有可選else語句的if語句:

if (isRaining()) {
  you.bringRainCoat();
} else if (isSnowing()) {
  you.wearJacket();
} else {
  car.putTopDown();
}
import

與Java一樣,使用import導入其他包。例如,Dart Web應用程序通常使用dart:html庫,可以這樣導入:

import 'dart:html';

如果只是想導入某個包下的某個dart文件,可以這樣導入:

import 'package:test/test.dart'; //指定導入test.dart(類似於Java中的test.java)
static

使用static關鍵字實現類範圍的變量和方法

static變量(只有在使用的時候纔會進行初始化):

class Queue {
  static const initialCapacity = 16;
  // ···
}

void main() {
  assert(Queue.initialCapacity == 16);
}

static方法:

import 'dart:math';

class Point {
  num x, y;
  Point(this.x, this.y);

  static num distanceBetween(Point a, Point b) {
    var dx = a.x - b.x;
    var dy = a.y - b.y;
    return sqrt(dx * dx + dy * dy);
  }
}

void main() {
  var a = Point(2, 2);
  var b = Point(4, 4);
  var distance = Point.distanceBetween(a, b); //靜態方法,不用實例化
  assert(2.8 < distance && distance < 2.9);
  print(distance);
}
assert

斷言assert(條件);
如果條件爲返回false,使用assert語句可以中斷正常執行, 代碼:

// text等於null時中斷
assert(text != null);

// number > 100時中斷
assert(number < 100);

// 如果urlString不是以"https"開頭
assert(urlString.startsWith('https'));

如果要附加一個消息到斷言,可在第二個參數輸入一個字符串:

assert(urlString.startsWith('https'),
    'URL ($urlString) should start with "https".');
enum

枚舉類型(通常稱爲枚舉或枚舉)是一種特殊類型,用於表示固定數量的常量值。
使用enum關鍵字聲明枚舉類型, 例如:

enum Color { red, green, blue }

枚舉中的每個值都有一個索引getter,它返回枚舉聲明中值的從零開始的位置。 例如,第一個值具有索引0,第二個值具有索引1:

  print('red index: \${Color.red.index}'); // -> 打印red index: 0
  print('green index: \${Color.green.index}'); // -> 打印: green index: 1
  print('blue index: \${Color.blue.index}');· //-> 打印: blue index: 2

要獲取枚舉中所有值的列表,可以使用以下方法:

List<Color> colors = Color.values;

您可以在switch語句中使用枚舉,如果您不處理所有枚舉值,您將收到警告:

var aColor = Color.blue;

switch (aColor) {
  case Color.red:
    print('Red as roses!');
    break;
  case Color.green:
    print('Green as grass!');
    break;
  default: // 沒有這行代碼的話,會有一個警告
    print(aColor); // 'Color.blue'
}

枚舉類型具有以下限制:

1.不能子類化,混合或實現枚舉。
2.無法顯式實例化枚舉。

for & in

可以使用標準for循環進行迭代, 例如:

var message = StringBuffer('Dart is fun');
for (var i = 0; i < 5; i++) {
  message.write('!');
}

像List和Set這樣的可迭代類支持使用的for-in形式迭代:

var list = [0, 1, 2];
  for (var x in list) {
    print(x); // 0 1 2
  }

等同於:

  for (int i = 0; i < list.length; i++){
    print(list[i]); // 0 1 2
  }
extend & super

使用extends來繼承一個類,使用super來調用父類:

class Television {
  void turnOn() {
    _illuminateDisplay();
    _activateIrSensor();
  }
  // ···
}

class SmartTelevision extends Television {
  void turnOn() {
    super.turnOn(); //調用父類方法
    _bootNetworkInterface();
    _initializeMemory();
    _upgradeApps();
  }
  // ···
}
async & await
  • async -> 異步
  • await -> 等待

Dart庫中包含許多返回Future或Stream對象的函數,關於Future和Steam會在後續進行講解,這裏暫不深究。 這些函數是異步的:它們在設置可能耗時的操作(例如I/O)後返回,而不等待該操作完成。

async和await關鍵字用於異步編程

async關鍵字修飾一個方法,要求必須返回一個Future對象,下面爲代碼例子:

//async關鍵字聲明該函數內部有代碼需要延遲執行
Future<String> getResult() async { 
  return await getResultFromDb(); //await關鍵字聲明運算爲延遲執行,然後返回運算結果
}

Future<String> getResultFromDb() {
  // 很多延時操作
  // 很多延時操作
  // 很多延時操作
  // 很多延時操作
  return new Future((){
      return 'This is server...';
  });
}

//打印:result = This is server...
 print(getResult().then((result){
      print('result = $result');
  }));
export

我們來看一個官方的http庫的代碼: http: ^0.12.0


我們可以看到該包下面有一個src目錄,還有一些其他的dart文件,Dart庫中,lib/src下的代碼被認爲是私有的, lib下的文件爲對外公開即外部可以使用的,我們可以看到該http庫下browser_client.dart, http.dart, io_client.dart, testing.dart是公開的API,我們拿其中一個,這裏我們拿http.dart文件看看

export 'src/base_client.dart';
export 'src/base_request.dart';
export 'src/base_response.dart';
export 'src/byte_stream.dart';
export 'src/client.dart';
export 'src/exception.dart';
export 'src/multipart_file.dart';
export 'src/multipart_request.dart';
export 'src/request.dart';
export 'src/response.dart';
export 'src/streamed_request.dart';
export 'src/streamed_response.dart';

可以看到export了幾個文件,即導出了這幾個文件,使外部這幾個文件的api,這時我們導入http來使用一下:

import 'package:http/http.dart' as http;

可以看到導入的幾個文件的類都可用了,那我們再找一個沒export的文件來看看外部是否可用,我們拿browser_client.dart來看看,其中有一個類:


我們在外部使用的時候:


是會報錯的,因爲該類並沒有export,即外部不可使用。

interface

已移除

switch & case & default

Dart中的switch語句可使用整數,字符串或編譯時常量, 以下爲使用字符串代碼示例:

var command = 'OPEN';
switch (command) {
  case 'CLOSED':
    executeClosed();
    break;
  case 'PENDING':
    executePending();
    break;
  case 'APPROVED':
    executeApproved();
    break;
  case 'DENIED':
    executeDenied();
    break;
  case 'OPEN':
    executeOpen();
    break;
  default: //表示其他值的條件
    executeUnknown();
}
sync & yield
  • sync 同步
  • yield 生成

當我們需要懶惰地(不需要很多手動定義可迭代類時複雜的公式化代碼)生成一系列值時,可以考慮使用生成器函數, Dart內置支持兩種生成器函數:

  • 同步生成器:返回一個Iterable對象
  • 異步生成器:返回Stream對象

同步生成器:將函數體標記爲sync *,並使用yield語句來賦值,下面例子爲返回 0-n 的迭代器:

Iterable<int> naturalsTo(int n) sync* {
  print('start');
  int k = 0;
  while (k < n) yield k++;
  print('end');
}

//使用
void main() {
  var it = naturalsTo(5).iterator;
  while(it.moveNext()) {
    print(it.current);
  }
}

//打印
start
value = 0
value = 1
value = 2
value = 3
value = 4
end

調用方法naturalsTo時,會馬上返回Iterable,且可以獲取迭代器iterator,但是,在調用遍歷之前,naturalsTo函數主體並不會立即執行,這裏我們可以看到調用var it = naturalsTo(5).iterator的時候沒有任何打印,並且我們可以看到,在遍歷打印的時候,先調用start,當把所有值打印完了,再打印end。說明調用naturalsTo得到這個Iterable的iterator的時候,yield會在你每次調用moveNext進行遍歷的時候產生一個值。當函數運行到yield的時候,yield這裏聲明一個求值表達式,返回值後,函數會在下一次moveNext的時候繼續執行函數體。

異步生成器函數,將函數體標記爲async *,並使用yield語句來傳遞值:

Stream<int> asynchronousNaturalsTo(int n) async* {
  int k = 0;
  while (k < n) yield k++;
}

//使用
void main() {
  asynchronousNaturalsTo(5).listen((v) {
    print(v);
  });
}

//打印
start
0
1
2
3
4
end

使用異步生成器返回數據流string,和sync*一樣,調用asynchronousNaturalsTo會立即返回Stream,但是只有在listen監聽數據流的時候纔會調用asynchronousNaturalsTo函數體,並且通過yield聲明求值表達式來計算對應的值。

如果生成器內部使用遞歸的話,可以使用yield *來提高其性能:

Iterable<int> naturalsDownFrom(int n) sync* {
  if (n > 0) {
    yield n;
    yield* naturalsDownFrom(n - 1);
  }
}

//使用
void main() {
  print(naturalsDownFrom(5));
}

//打印
(5, 4, 3, 2, 1)

naturalsDownFrom函數還是返回一個Iterable,當參數爲5時,5 > 0時,先執行yield 5, 這時迭代器首先會產生一個值5,然後再通過yield*生成新的值,並且加到當前迭代器中。

break & continue

跳出循環

while (true) {
  if (shutDownRequested()) break;
  processIncomingRequests();
}

跳到下一個循環迭代

or (int i = 0; i < candidates.length; i++) {
  var candidate = candidates[i];
  if (candidate.yearsExperience < 5) {
    continue;
  }
  candidate.interview();
}
external

表示代碼的實現由外部提供,我們定義一個類:

class Object {
  const Object();

  external bool operator ==(other);
  external int get hashCode;
  external String toString();
}

//使用
void main() {
  Object object = new Object();
  print('to string = ${object.toString()}');
}

//打印
to string = null
//但是如果我們將toString去掉的話,則會打印
to string = Instance of 'Object' //dart中默認的toString打印

external聲明瞭這些方法需要由外部去實現,若外部沒實現,則會返回null

library & part

使用library關鍵字可以定義一個庫的名字,我們這裏自定義一個庫來說明一下這兩個關鍵字:



這裏分別有3個文件,我們看看:
main.dart

library main; //定義當前庫的名字爲main
import 'dart:math' as math; 

//聲明以下兩個文件屬於main庫
part 'test/lib/liba.dart'; 
part 'test/lib/libb.dart';

class LibMain{
    static int max(int a, int b) => math.max(a, b);

    static String getParts() => LibA.TAG + ", " + LibB.TAG;
}

liba.dart

part of main;  //聲明屬於main庫

class LibA{
  static String TAG = 'liba';
}

libb.dart

part of main; //聲明屬於main庫

class LibB{
  static String TAG = 'libb';
}

再導入main之後,

import 'lib/main.dart';

liba.dart和libb.dart中聲明的類外部可用:


part可以將庫拆分爲多個Dart文件,但是建議儘量避免使用其來創建庫,因爲這樣會使代碼很難閱讀和修改。建議直接直接在lib/<包名> .dart下創建一個“main”庫文件,用一個main文件來管理所有公共API。

this

和Java中this相類似,用this關鍵字來引用當前實例

factory

用來修飾構造函數,描述該構造函數作爲一個工廠構造函數功能,在實現不用總是創建新實例的構造函數的時候,可以使用factory關鍵字,例如下面例子中,可能從緩存中返回實例,或者子類的實例。

class Logger {
  final String name;
  bool mute = false;

  //一個維護Logger類的map
  static final Map<String, Logger> _cache =
      <String, Logger>{};

  //根據不同的name獲取對應的Logger,
  factory Logger(String name) {
    if (_cache.containsKey(name)) {
      return _cache[name];
    } else {
      final logger = Logger._internal(name);
      _cache[name] = logger;
      return logger;
    }
  }

  //一個內部構造函數
  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) print(msg);
  }
}

注意,使用factory修飾的構造函數不能使用this,類似於在Java的static函數中,不能使用this

mixin & with & on

Dart 2.1中引入了對mixin關鍵字的支持, 我們可以看到官方的描述:Mixins是一種在多個類層次結構中重用類代碼的方法。關鍵信息:

  • 多個類層次結構
  • 重用類代碼

我這裏只簡單描述下該關鍵字的作用和使用方法:

  • mixin字面意思爲混入的意思,要使用mixin的話,需要使用with關鍵字,後跟一個或多個mixin的名稱, 可視爲混入多個類
  • 有時候我們需要創建一個類,這個類需要使用不同類的不同的方法的時候,就需要使用mixin方法,,因爲Dart中只能繼承一個類,而且使用接口的話,必須在其他類也實現這個接口,下面我們使用我們看下下面的例子:
//程序員喜歡寫代碼
class Programmer{
  code(){
    print('I am a programmer, i like coding.');
  }
}

//歌唱家喜歡唱歌
class Singer{
  singing(){
    print('I am a singer, i like singing.');
  }
}

//既愛編碼,也愛唱歌
class Mixin with Programmer, Singer{

}

void main() {
  Mixin mixin = Mixin();
  mixin.code();
  mixin.singing();
}

//打印:
I am a programmer, i like coding.
I am a musician, i like singing.

注意:這裏類Programmer和Singer不能聲明構造函數包括命名函數:


當我們使用mixin調用不同類相同接口結果會是怎樣呢,我們看下面代碼:

class A {
  name(){
    print('I am a student.');
  }
}

class B{
  name(){
    print('I am a teacher.');
  }
}

class AB with A, B{

}

class BA with B, A{

}

void main() {
  new AB().name();

  new BA().name();
}

//打印:
I am a teacher.
I am a student.

可以看到接口相同的情況這裏是name,最終會調用with的最後一個類的接口。

如果我們需要限定,什麼類才能被混入,可以使用mixin+on的方法限定:

//mixn定義了類Flutter要求只有實現Programmer類的才能被混入
mixin Flutter on Programmer{
  flu(){
    print('This is in flutter.');
  }
}

//會如下面圖片報錯
//class A  with Flutter{
//}

//可以混入
class B extends Programmer with Flutter{
}

new B ().flu();  //打印This is in flutter.

最後,關於mixin更詳細的解釋可以參考:

throw

拋出異常:

throw FormatException('Expected at least 1 section');

也可以拋出任何任意對象異常:

throw 'Out of llamas!';
class Throw{
  @override
  String toString() {
    return 'Are you ok?';
  }
}

void main() {
  throw new Throw();
}
try & catch & finally & on & rethrow

異常捕獲:

//普通使用
void main() {
  try{
    throw "You are wrong.";
  }catch (e){
    print('catch exception: '+e);
  }
}

//打印:
catch exception: You are wrong.

使用on可以捕獲某種異常

class ExceptionA{
  @override
  String toString() {
    return 'This is exception a.';
  }
}

class ExceptionB{
  @override
  String toString() {
    return 'This is exception b.';
  }
}

throwCatchException(Object object){
  try{
    throw object;
  } on ExceptionA{ //指定某種異常類
    print("It's exception a.");
  } on ExceptionB catch(e){  //指定某種異常類並獲取異常對象
    print(e);
  } on Exception catch (e){
    print(e);
  }
}

void main() {
  throwCatchException(new ExceptionA());
  throwCatchException(new ExceptionB());
  throwCatchException(new Exception(''));
}
//打印:
It's exception a.
This is exception b.
Exception: 

可以爲catch()指定一個或兩個參數, 第一個是拋出的異常對象,第二個是堆棧跟蹤(StackTrace對象):

void main() {
  try{
    throw 'This is a exception.';
  }catch (e, s){
    print('e ${e}');
    print('s ${s}');
  }
}
//打印:
e This is a exception.
s #0      main (file:///E:/flutter/projects/flutter/test/test.dart:5:5)
#1      _startIsolate.<anonymous closure> (dart:isolate/runtime/libisolate_patch.dart:289:19)
#2      _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:171:12)

如果想異常可傳播, 使用rethrow接口

void misbehave() {
  try {
    dynamic foo = true;
    print(foo++); // 運行時出錯
  } catch (e) {
    print('misbehave() partially handled ${e.runtimeType}.');
    rethrow;  //允許調用者可以看到異常
  }
}

void main() {
  try {
    misbehave();
  } catch (e) {
    print('main() finished handling ${e.runtimeType}.');
  }
}
//打印:
misbehave() partially handled NoSuchMethodError.
main() finished handling NoSuchMethodError.

無論是否拋出異常,要確保某些代碼運行,請使用finally子句。 如果沒有catch子句與異常匹配,則在finally子句運行後拋出異常:

class ExceptionA{
  @override
  String toString() {
    return 'This is exception a.';
  }
}

class ExceptionB{
  @override
  String toString() {
    return 'This is exception b.';
  }
}

finallyMethod(){
  print('finally method.');
}

void main() {
  try {
    throw new ExceptionA();
  } on ExceptionB catch (e) {
    print(e);
  } finally{
    finallyMethod();
  }
}

//打印:
finally method.
Unhandled exception:
This is exception a.
#0      main (file:///E:/flutter/projects/flutter/test/test.dart:21:5)
#1      _startIsolate.<anonymous closure> (dart:isolate/runtime/libisolate_patch.dart:289:19)
#2      _RawReceivePortImpl._handleMessage (dart:isolate/runtime/libisolate_patch.dart:171:12)
false & true

爲了表示布爾值,Dart有一個名爲bool的類型, 只有兩個對象具有bool類型: true和false,它們都是編譯時常量

new

創建類實例

class

聲明一個類

final & const

final聲明一個變量只能初始化一次,和java用法相同

const聲明一個是編譯時常量的變量, 常量變量不能進行賦值,如果const變量在類級別,則將其標記爲static const。
當我們想讓一個變量不被改變,可以聲明爲const變量。

typedef

typedef用於給函數類型指定名稱,因爲在Dart中,函數也是一個對象,通常用Function泛指所有函數,我們來看一下下面的例子(沒有使用函數別名):


class SortedCollection {
  Function compare;
  
  //這裏需要傳遞一個返回值爲int,參數爲(Object, Object)的函數
  SortedCollection(int f(Object a, Object b)) {
    compare = f;
  }
}

//一個返回值爲int,參數爲(Object, Object)的函數
int sort(Object a, Object b) => 0;

void main() {
  SortedCollection coll = SortedCollection(sort);

  // 我們都知道compare是一個函數
  // 但是我們知道它是什麼類型的函數嗎,意味着我們只知道它是一個函數,但是是什麼類型的函數我們不知道
  assert(coll.compare is Function); // 這裏毫無疑問是true, 即不會中斷執行
}

我們可以用typedef來聲明一個函數類型:

 //定義一個函數類型爲compare,其類型爲
typedef Compare = int Function(Object a, Object b);

class SortedCollection {
  Compare compare;

  SortedCollection(this.compare);
}

//一個返回值爲int,參數爲(Object, Object)的函數即類型爲Compare的函數
int sort(Object a, Object b) => 0;

void main() {
  SortedCollection coll = SortedCollection(sort);
  assert(coll.compare is Function);  //True
  assert(coll.compare is Compare); //True
}

目前typedef只能用於聲明函數類型,

operator

如果你想定義一個Vector類(向量類),可以使用以下運算符:

< + | []
> / ^ []=
<= ~/ & ~
>= * << ==
- % >>

下面爲一個覆蓋+和 - 運算符的類的示例:

class Vector {
  final int x, y;

  Vector(this.x, this.y);

  Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
  Vector operator -(Vector v) => Vector(x - v.x, y - v.y);

// Operator == and hashCode not shown. For details, see note below.
// ···
}

void main() {
  final v = Vector(2, 3);
  final w = Vector(2, 3);
  final vw = v + w;
  final ww = v - w;

  print('vw -> (${vw.x}, ${vw.y})');
  print('ww -> (${ww.x}, ${ww.y})');
}

//打印:
vw -> (4, 6)
ww -> (0, 0)
var

使用var聲明一個變量但是不指定其類型

var name = 'Bob'; //聲明一個變量爲字符串變量

但是一旦聲明賦值了爲一個類型候,不能再分配給另一個類型


covariant

我們在繼承一個類的時候,在重構一個方法時,強制將其參數由父類縮窄成其子類的話,會提示錯誤,例子:

//定義一個Animal類,其函數chase參數爲Animal
class Animal {
  void chase(Animal x) {}
}

class Mouse extends Animal {
  getName(){
    return 'mouse';
  }
}

class Cat extends Animal {
  //強制將chase函數的Animal參數縮窄成Mouse 

  void chase(Mouse mouse) {
    print('cat chase ${mouse.getName()}');
  }
}

//報錯, 提示重寫類型不匹配
test/test.dart:12:20: Error: The parameter 'mouse' of the method 'Cat::chase' has type #lib1::Mouse, which does not match the corresponding type in the overridden method (#lib1::Animal).
Change to a supertype of #lib1::Animal (or, for a covariant parameter, a subtype).
  void chase(Mouse mouse) {
                   ^
test/test.dart:2:8: Context: This is the overridden method ('chase').
  void chase(Animal x) {}
       ^

使用covariant關鍵字後:

class Animal {
  void chase(Animal x) {}
}

class Mouse extends Animal {
  getName(){
    return 'mouse';
  }
}

class Cat extends Animal {
  void chase(covariant Mouse mouse) {
    print('cat chase ${mouse.getName()}');
  }
}

void main(){
  new Cat().chase(new Mouse());
}
//打印
cat chase mouse

covariant字義爲協變,即我和編譯器協商,這個參數縮窄變化是我故意這樣做的,你別拋異常了。

Function

Dart是一種真正的面嚮對象語言,因此即使是函數也是對象並且具有類型Function。 這意味着函數可以分配給變量或作爲參數傳遞給其他函數。

//定義一個add函數
int add(int a, int b) => a+b;

handle(Function function){
  print('handle: ${function(1, 2)}'); //打印function的結果
}

void main(){
  handle(add); //調用handle並傳遞add函數
}
//打印
handle: 3
void

在Dart 1中,void僅可用作函數的返回類型(例如void main()),但在Dart 2中它已被推廣,並且可在其他地方使用, 例如Future <void>

void類型不能用於任何東西,且將某些東西分配給void類型是無效的:

void foo() {}
void main() {
  var bar = foo(); // 無效
}

The expression here has a type of 'void', and therefore cannot be used. -> 此表達式的類型爲“void”,無法使用

在函數中,表示該函數無需返回值。

在實踐中,一般使用void來表示“任何我不關心元素”,或者更常見的是,表示“省略”,例如在Future <void>或Stream <void>中。

get & set

在Java中,getter和setter應該是蠻令我們頭疼,如果一些類的屬性足夠多的話,提供getter和setter接口後,文件很輕鬆可以達到成千上百行。但是在Dart中,提供了set和get關鍵子來提供對象屬性的讀寫訪問權限。

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

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

  //定義兩個可計算的屬性right 和bottom.
  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);
  
  print('right: ${rect.right}');
  rect.right = 100;
  print('right: ${rect.right}');

  print('bottom: ${rect.bottom}');
  rect.bottom = 120;
  print('bottom: ${rect.bottom}');
}

//打印:
right: 23
right: 100
bottom: 19
bottom: 120

在Dart中,使用get和set實現getter和setter功能,是不是簡潔多了呢

使用get和set後,我們可以從實例變量開始,在get/set的方法中用方法封裝,而無需更改外部調用的代碼。

while & do while

while: 在循環執行之前計算條件

while (!isDone()) {
  doSomething();
}

do-while: 在循環開始執行後計算條件

do {
  printLine();
} while (!atEndOfPage());
deferred

deferred用於聲明一個延遲加載一個庫,通常叫懶加載。允許你只有在需要使用該庫的時候,再加載該庫。

//文件calculate.dart
class Calculate{

  static String name = "Cal";

  static printf(String s){
    print('cal: $s');
  }

  int add(int a, int b) => a + b;
}

//文件test.dart
import 'calculate.dart' deferred as cal; //聲明該庫會延遲加載且命名爲cal

void main() {
  print('1');
  greet();
  print('2');
  print('3');
}

//異步加載庫calculate.dart
//加載完畢後再進行操作
Future greet() async {
  await cal.loadLibrary();
  print('cal name: ${cal.Calculate.name}');
  print('add(1, 2) = ${new cal.Calculate().add(1, 2)}');
  cal.Calculate.printf('ss');
}

//打印:
1
2
3
cal name: Cal
add(1, 2) = 3
cal: ss
return

用於在函數中返回一個值

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章