Flutter日漸火爆,所以在進行Flutter學習前先學習一些其所使用的開發語言dart的基礎,這篇文章主要學習了html
// 定義一個方法。 printInteger(int aNumber) { print('The number is $aNumber.'); // Print to console. } // main入口函數。 main() { var number = 42; // 聲明並初始化變量。 printInteger(number); // 函數調用。 } 複製代碼
dart註釋方法,更多註釋能夠看個人另外一篇文章https://www.jianshu.com/p/d1dae0d5c472java
數據類型,更多數據類型可看https://www.dartlang.org/guides/language/language-tour#built-in-types程序員
一種方便的顯示輸出方式express
字符串,dart中更推薦使用**'...'**編程
字符串插值:包括字符串文字內部的變量或表達式的字符串api
一種聲明變量而不指定其類型的方法,關鍵字之一緩存
當要學習dart時,請記住如下事實和概念:bash
一切皆爲對象,放在變量中的全部內容都是一個對象,每一個對象都是一個class的實例,numbers,函數和null都是對象,全部對象都繼承自Object類。下面給個圖你看一下,沒錯,連int都是對象: markdown
儘管Dart是強類型的,但類型註釋是可選的,由於Dart能夠推斷類型。在上面的代碼中,數字被推斷爲int類型。若是要明確說明不須要任何類型,請使用特殊類型dynamic。異步
Dart支持泛型類型,如List(整數列表)或List(任何類型的對象列表)。
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 |
詳細可看下面說明
使用abstract修飾符定義抽象類即沒法實例化的類,抽象類能夠自定義一些接口。抽象類一般有抽象方法,下面是一個聲明具備抽象方法的抽象類的示例:
// 該類聲明爲抽象類且不可實例化。
abstract class AbstractContainer {
// 定義構造函數,變量,方法等...
// 其餘....
// 抽象方法。
void updateChildren();
}
複製代碼
下面爲實現抽象方法的例子:
//抽象類 abstract class Doer { void doSomething(); // 定義一個抽象方法 } //繼承抽象類實現抽象方法 class EffectiveDoer extends Doer { void doSomething() { // 實現邏輯 } } 複製代碼
顧名思義,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的類型系統,或者值來自互操做或者在靜態類型系統範圍以外的狀況。
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 {...}
複製代碼
有時候咱們導入一個庫,若是隻想使用庫的一部分,則能夠有選擇地導入庫,例如:
// 只導入foo import 'package:lib1/lib1.dart' show foo; 複製代碼
//導入整個庫除了foo import 'package:lib2/lib2.dart' hide foo; 複製代碼
as,is,和 !is 運算符在運行時檢查類型很方便
代碼示例:
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(); 複製代碼
與Java或其餘語言同樣,Dart支持帶有可選else語句的if語句:
if (isRaining()) { you.bringRainCoat(); } else if (isSnowing()) { you.wearJacket(); } else { car.putTopDown(); } 複製代碼
與Java同樣,使用import導入其餘包。例如,Dart Web應用程序一般使用dart:html庫,能夠這樣導入:
import 'dart:html'; 複製代碼
若是隻是想導入某個包下的某個dart文件,能夠這樣導入:
import 'package:test/test.dart'; //指定導入test.dart(相似於Java中的test.java) 複製代碼
使用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(條件); 若是條件爲返回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 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循環進行迭代, 例如:
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 } 複製代碼
使用extends來繼承一個類,使用super來調用父類:
class Television { void turnOn() { _illuminateDisplay(); _activateIrSensor(); } // ··· } class SmartTelevision extends Television { void turnOn() { super.turnOn(); //調用父類方法 _bootNetworkInterface(); _initializeMemory(); _upgradeApps(); } // ··· } 複製代碼
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'); })); 複製代碼
咱們來看一個官方的http庫的代碼: http: ^0.12.0
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,即外部不可以使用。
已移除
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(); } 複製代碼
當咱們須要懶惰地(不須要不少手動定義可迭代類時複雜的公式化代碼)生成一系列值時,能夠考慮使用生成器函數, Dart內置支持兩種生成器函數:
同步生成器:將函數體標記爲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*生成新的值,而且加到當前迭代器中。
跳出循環
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(); } 複製代碼
表示代碼的實現由外部提供,咱們定義一個類:
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關鍵字能夠定義一個庫的名字,咱們這裏自定義一個庫來講明一下這兩個關鍵字:
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。
和Java中this相相似,用this關鍵字來引用當前實例
用來修飾構造函數,描述該構造函數做爲一個工廠構造函數功能,在實現不用老是建立新實例的構造函數的時候,可使用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
Dart 2.1中引入了對mixin關鍵字的支持, 咱們能夠看到官方的描述:Mixins是一種在多個類層次結構中重用類代碼的方法。關鍵信息:
我這裏只簡單描述下該關鍵字的做用和使用方法:
//程序員喜歡寫代碼 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 FormatException('Expected at least 1 section'); 複製代碼
也能夠拋出任何任意對象異常:
throw 'Out of llamas!'; 複製代碼
class Throw{ @override String toString() { return 'Are you ok?'; } } void main() { throw new Throw(); } 複製代碼
異常捕獲:
//普通使用 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) 複製代碼
爲了表示布爾值,Dart有一個名爲bool的類型, 只有兩個對象具備bool類型: true和false,它們都是編譯時常量
建立類實例
聲明一個類
final聲明一個變量只能初始化一次,和java用法相同
const聲明一個是編譯時常量的變量, 常量變量不能進行賦值,若是const變量在類級別,則將其標記爲static const。 當咱們想讓一個變量不被改變,能夠聲明爲const變量。
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只能用於聲明函數類型,
若是你想定義一個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 name = 'Bob'; //聲明一個變量爲字符串變量 複製代碼
可是一旦聲明賦值了爲一個類型候,不能再分配給另外一個類型
咱們在繼承一個類的時候,在重構一個方法時,強制將其參數由父類縮窄成其子類的話,會提示錯誤,例子:
//定義一個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字義爲協變,即我和編譯器協商,這個參數縮窄變化是我故意這樣作的,你別拋異常了。
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 複製代碼
在Dart 1中,void僅可用做函數的返回類型(例如void main()),但在Dart 2中它已被推廣,而且可在其餘地方使用, 例如Future
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 或Stream 中。
在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: 在循環執行以前計算條件
while (!isDone()) { doSomething(); } 複製代碼
do-while: 在循環開始執行後計算條件
do { printLine(); } while (!atEndOfPage()); 複製代碼
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 複製代碼
用於在函數中返回一個值