Dart語言是google開發的語言,目前對外主要用於flutter開發,Google內部使用Dart開發大型應用。Dart語言還是很受歡迎且很有發展潛力。它是一個純面向對象的語言,其語法比較靈活,也比較簡單。Dart語法跟java相似度比較高,但是又有很多不同。
Dart官方教程:http://dart.goodev.org/guides/language/language-tour
或https://dart.dev/guides/language/language-tour
搭建Dart開發環境
安裝Dart SDK:https://dart.dev/get-dart
安裝IDE:推薦使用Visual Studio Code: https://code.visualstudio.com/
一、數據類型
- int (一般佔8個字節)
- double (一般佔8個字節)
- num(int和double的父類型,一般佔8個字節)
- String
- bool
- List
- Map
用法示例:
var year = 1977; //定義int類型,注意dart沒有long類型。
int year2=1977; //同上,dart可以顯式聲明類型也可以自動推導類型。
var antennaDiameter = 3.7;//定義double類型,注意dart沒有float類型。
var name = 'Voyager I'; ////定義String類型
var flybyObjects = List();或 var flybyObjects = new List();
var flybyObjects = ['Jupiter', 'Saturn', 'Uranus', 'Neptune'];
var image=new Map();
var image = {
'tags': ['saturn'],
'url': '//path/to/saturn.jpg'
};
var map={"a":"1","b":2,"c":3,4:5};
print(map[4]);//打印key=4的元素
map["f"]="aaa"; //添加元素“f”="aaa"
print(map["f"]);//打印key=f的元素
var list=new List();
list.add("a");//添加元素:"a"
list.add(1);//添加元素:1
print(list); //打印所有元素
這裏沒有顯式的指定變量類型。也可以指定變量類型。
int year=1977;
double antennaDiameter = 3.7;
String name = 'Voyager I';
List flybyObjects = ['Jupiter', 'Saturn', 'Uranus', 'Neptune'];
Map image = {
'tags': ['saturn'],
'url': '//path/to/saturn.jpg'
};
dart語法跟js和java比較想像,跟js的不同是,js只有隱式類型聲明,沒有顯式類型類型聲明。跟java不同的是,java必須顯示聲明類型。
例如js聲明變量:var year = 1977;
js沒有這種聲明方式:int year = 1977;
dart與python變量聲明的不同是,python連var關鍵字都不需要,即year=1997
,並且語句後面不需要分號結束。
跟java的不同:
1、dart聲明沒有初始化的變量都會被賦予默認值 null,包括int和double、bool。
2、Dart 沒有 public、private、protected 這些關鍵字,變量名以"_"開頭意味着對它的 lib 是私有的。
二、流程控制語句
跟js和java幾乎一樣。
if (year >= 2001) {
print('21st century');
} else if (year >= 1901) {
print('20th century');
}
for (var object in flybyObjects) {
print(object);
}
for (int month = 1; month <= 12; month++) {
print(month);
}
while (year < 2016) {
year += 1;
}
三、普通函數(方法)
計算菲波拉契數列第n項:
int fibonacci(int n) {
if (n == 0 || n == 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}
var result = fibonacci(20);
可以看出定義函數部分跟java比較相似,而js需要添加function關鍵字修飾。
js聲明函數:
function functionname()
{
// 執行代碼
}
Dart 是一個真正的面嚮對象語言,方法也是對象並且具有一種 類型。 這意味着,方法可以賦值給變量,也可以當做其他方法的參數。 也可以把 Dart 類的實例當做方法來調用。
下面是定義方法的實例
bool isNoble(int atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
也可以忽略類型定義,自動推導類型。沒有返回值時爲void類型,void關鍵字可省。
isNoble(atomicNumber) {
return _nobleGases[atomicNumber] != null;
}
對於只有一個表達式的方法,你可以選擇 使用縮寫語法來定義。
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
這個 => expr 語法是 { return expr; } 形式的縮寫。=> 形式 有時候也稱之爲 胖箭頭 語法。
注意: 在箭頭 (=>) 和冒號; 之間只能使用一個 表達式 – 不能使用 語句。 例如:你不能使用 if statement,但是可以 使用條件表達式 conditional expression。
方法可以有兩種類型的參數:必需的和可選的。 必需的參數在參數列表前面, 後面是可選參數。
(1)可選參數
可選參數就是調用方法時實參是可選的,調用時可傳可不傳。可選參數分爲兩種,一種是是命名參數,一種是位置參數。
命名參數就是一對花括號{}括起來的參數,調用時實參必須指定參數的名字,必須以“name:value”這種形式傳遞。非命名參數就不能使用name:value形式傳遞。
位置參數就是中括號[]括起來的參數,裏面的參數可傳可不傳,但是傳的時候必須按照位置(順序)來傳。
但是這兩種參數不能同時當做可選參數,也就是一個方法不能同時有位置參數和命名參數。
沒有中括號或花括號的參數列表,就必須按參數位置(順序)傳遞並且是一個都不能少地傳遞。
Dart爲什麼定義這種可選參數呢?因爲Dart跟Java不同的是,Java可以使用多個同名的函數不同的參數來實現方法重載,而Dart就不允許一個類中有同名的函數,因此可選參數就類似於Java中的方法重載,最終的目的都是同一個方法,實現多種傳參。相對Java而言,Dart這種可選參數靈活度更高。
(1.1)命名參數
調用方法的時候,你必須使用這種形式 paramName: value 來指定命名參數。例如:
enableFlags("red",bold: true, hidden: false);
或
enableFlags("red"); //bold是null,hidden是null
在定義方法的時候,使用{param1, param2, …}
的形式來指定命名參數:
/// Sets the [bold] and [hidden] flags to the values
/// you specify.
enableFlags(String color,{bool bold, bool hidden}) {
// ...
}
(1.2)位置參數
把一些方法的參數放到 [] 中就變成可選 位置參數了:
String say(String from, String msg, [String device,String day]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device on $day';
}
return result;
}
下面是不使用可選參數調用上面方法 的示例
assert(say('Bob', 'Howdy') == 'Bob says Howdy');
下面是使用可選參數調用上面方法的示例:
assert(say('Bob', 'Howdy', 'smoke signal',"2019/10/1") == 'Bob says Howdy with a smoke signal on 2019/10/1');
(1.3)可選參數默認值
在定義方法的可選參數時,可以使用 = 來指定可選參數的默認值。 默認值只能是編譯時常量。 如果沒有提供默認值,則默認值爲 null。
下面是設置命名參數默認值的示例:
/// Sets the [bold] and [hidden] flags to the values you
/// specify, defaulting to false.
void enableFlags({bool bold = false, bool hidden = false}) {
// ...
}
// bold will be true; hidden will be false.
enableFlags(bold: true);
下面的示例顯示瞭如何設置位置參數的默認值:
String say(String from, String msg,[String device = 'carrier pigeon', String mood]) {
var result = '$from says $msg';
if (device != null) {
result = '$result with a $device';
}
if (mood != null) {
result = '$result (in a $mood mood)';
}
return result;
}
assert(say('Bob', 'Howdy') == 'Bob says Howdy with a carrier pigeon');
還可以使用 list 或者 map 作爲默認值。 下面的示例定義了一個方法 doStuff(), 並分別爲 list 和 gifts 參數指定了 默認值。
void doStuff(
{List<int> list = const [1, 2, 3],
Map<String, String> gifts = const {
'first': 'paper',
'second': 'cotton',
'third': 'leather'
}}) {
print('list: $list');
print('gifts: $gifts');
}
(2)入口函數
每個應用都需要有個頂級的 main() 入口方法才能執行。 main() 方法的返回值爲 void 並且有個可選的 List 參數。
下面是一個 web 應用的 main() 方法:
void main() {
querySelector("#sample_text_id")
..text = "Click me!"
..onClick.listen(reverseText);
}
注意: 前面代碼中的 … 語法爲 級聯調用(cascade)。 使用級聯調用語法, 你可以在一個對象上執行多個操作。
下面是一個命令行應用的 main() 方法,並且使用了 方法參數作爲輸入參數:
// Run the app like this: dart args.dart 1 test
void main(List<String> arguments) {
print(arguments);
assert(arguments.length == 2);
assert(int.parse(arguments[0]) == 1);
assert(arguments[1] == 'test');
}
(3)方法作爲參數調用另一個方法
可以把方法當做參數調用另外一個方法。例如:
printElement(element) {
print(element);
}
var list = [1, 2, 3];
// Pass printElement as a parameter.
list.forEach(printElement);
方法也可以賦值給一個變量
var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');
上面的方法爲 下面即將介紹的匿名方法。
(4)匿名方法
大部分方法都帶有名字,例如 main() 或者 printElement()。 你有可以創建沒有名字的方法,稱之爲 匿名方法,有時候也被稱爲 lambda 或者 closure 閉包。 你可以把匿名方法賦值給一個變量, 然後你可以使用這個方法,比如添加到集合或者從集合中刪除。匿名方法的使用場景很多,例如List的一些方法常常需要傳一個匿名方法作爲參數。
匿名函數和命名函數看起來類似— 在括號之間可以定義一些參數,參數使用逗號 分割,也可以是可選參數。 後面大括號中的代碼爲函數體:
([[Type] param1[, …]]) {
codeBlock;
};
下面的代碼定義了一個參數爲i (該參數沒有指定類型)的匿名函數。 list 中的每個元素都會調用這個函數來 打印出來,同時來計算了每個元素在 list 中的索引位置。
var list = ['apples', 'oranges', 'grapes', 'bananas', 'plums'];
list.forEach((i) {
print(list.indexOf(i).toString() + ': ' + i);
});
運行結果:
0: apples
1: oranges
2: grapes
3: bananas
4: plums
(5)靜態作用域
Dart 是靜態作用域語言,變量的作用域在寫代碼的時候就確定過了。 基本上大括號裏面定義的變量就 只能在大括號裏面訪問,和 Java 作用域 類似。
下面是作用域的一個 示例:
var topLevel = true;
main() {
var insideMain = true;
myFunction() {
var insideFunction = true;
nestedFunction() {
var insideNestedFunction = true;
assert(topLevel);
assert(insideMain);
assert(insideFunction);
assert(insideNestedFunction);
}
}
}
注意 nestedFunction() 可以訪問所有的變量, 包含頂級變量。
(6)詞法閉包
一個閉包是一個方法對象,不管該對象在何處被調用, 該對象都可以訪問其作用域內的變量。
方法可以封閉定義到其作用域內的變量。 下面的示例中,makeAdder() 捕獲到了變量 addBy。 不管你在哪裏執行 makeAdder() 所返回的函數,都可以使用 addBy 參數。
/// Returns a function that adds [addBy] to the
/// function's argument.
Function makeAdder(num addBy) {
return (num i) => addBy + i;
}
main() {
// Create a function that adds 2.
var add2 = makeAdder(2);
// Create a function that adds 4.
var add4 = makeAdder(4);
assert(add2(3) == 5);
assert(add4(3) == 7);
}
(7)測試函數是否相等
下面是測試頂級方法、靜態函數和實例函數 相等的示例:
foo() {} // A top-level function
class A {
static void bar() {} // A static method
void baz() {} // An instance method
}
main() {
var x;
// Comparing top-level functions.
x = foo;
assert(foo == x);
// Comparing static methods.
x = A.bar;
assert(A.bar == x);
// Comparing instance methods.
var v = new A(); // Instance #1 of A
var w = new A(); // Instance #2 of A
var y = w;
x = w.baz;
// These closures refer to the same instance (#2),
// so they're equal.
assert(y.baz == x);
// These closures refer to different instances,
// so they're unequal.
assert(v.baz != w.baz);
}
(8)返回值
所有的函數都返回一個值。如果沒有指定返回值,則 默認把語句 return null; 作爲函數的最後一個語句執行。
四、類定義
class Spacecraft {
String name;
DateTime launchDate;
int launchYear;
// Constructor, including syntactic sugar for assignment to members.
Spacecraft(this.name, this.launchDate) {
// Pretend the following is something you'd actually want to run in
// a constructor.
launchYear = launchDate?.year;
}
// Named constructor that forwards to the default one.
Spacecraft.unlaunched(String name) : this(name, null);
// Method.
void describe() {
print('Spacecraft: $name');
if (launchDate != null) {
int years = new DateTime.now().difference(launchDate).inDays ~/ 365;
print('Launched: $launchYear ($years years ago)');
} else {
print('Unlaunched');
}
}
}
可以這樣使用上面定義的類:
var voyager = new Spacecraft('Voyager I', new DateTime(1977, 9, 5));
voyager.describe();
var voyager3 = new Spacecraft.unlaunched('Voyager III');
voyager3.describe();
五、構造函數
(1)、默認構造函數
默認構造函數即不需要額外定義的構造函數
main() {
Person person= new Person();
}
class Person {
String name;
int age;
}
也可以顯示指定構造函數
class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
}
上面這個構造函數可以簡單寫成下面這樣。
class Person {
String name;
int age;
Person(this.name, this.age);
}
dart不允許有同名的構造函數,普通函數之間也不能同名。例如下面的代碼會編譯不通過。
class Person {
String name;
int age;
Person(){
}
Person(String name,int age){
}
}
那麼如何實現構造函數重載呢?請往下看。
(2)、命名構造函數
main() {
Person person1 = new Person("kitty",10);
Person person2 = new Person.onlyName("Michale");
Person person3 = new Person.onlyAge(12);
print("person1: name=${person1.name},age=${person1.age}");
print("person2: name=${person2.name},age=${person2.age}");
print("person3: name=${person3.name},age=${person3.age}");
}
class Person {
String name;
int age;
Person(this.name, this.age);
Person.onlyName(this.name);
Person.onlyAge(this.age);
}
上面Person.onlyName和Person.onlyAge就是命名構造函數。相當於是構造函數重載。當然還可以使用命名參數和位置參數實現構造函數重載。
運行結果:
person1: name=kitty,age=10
person2: name=Michale,age=null
person3: name=null,age=12
(3)、重定向構造函數
重定向構造函數,就是將函數的實現委託給另一個構造函數(不是父類的構造函數),並且自身不允許有實現。
main() {
Person person1 = new Person("kitty",10);
Person person2 = new Person.onlyName("Michale");
print("person1: name=${person1.name},age=${person1.age}");
print("person2: name=${person2.name},age=${person2.age}");
}
class Person {
String name;
int age;
Person(this.name, this.age);
Person.onlyName(String _name):this(_name,12); //不允許有函數體,否則編譯不通過。
}
運行結果:
person1: name=kitty,age=10
person2: name=Michale,age=12
跟java一樣,dart語言中,構造函數也是不能被子類繼承。
如果父類沒有無參構造函數,那麼子類必須顯式聲明一個構造函數,並且要顯式調用父類的構造函數。
這一點跟java是一樣的,語法上不同的是,子類只需要在函數簽名後加上":super(…)"。
main() {
Employee employee1 = new Employee("kitty",10,8000);
print("employee1: name=${employee1.name},age=${employee1.age},salary=${employee1.salary}");
}
class Person {
String name;
int age;
Person(this.name, this.age) {
print("super construted");
}
}
class Employee extends Person {
double salary;
Employee(String name, int age, this.salary) : super(name, age) {
print("child construted");
}
}
運行結果:
super construted
child construted
employee1: name=kitty,age=10,salary=8000.0
下面代碼跟上面代碼等同。
class Person {
String name;
int age;
Person(this.name, this.age);
}
class Employee extends Person {
double salary;
Employee(String name, int age, this.salary) : super(name, age);
}
(4)初始化列表
除了調用超類構造函數外,您還可以在構造函數主體運行之前初始化實例變量。用逗號分隔初始化代碼。
// Initializer list sets instance variables before
// the constructor body runs.
Point.fromJson(Map<String, num> json)
: x = json['x'],
y = json['y'] {
print('In Point.fromJson(): ($x, $y)');
}
在開發過程中,您可以通過assert在初始化列表中使用來驗證輸入。
Point.withAssert(this.x, this.y) : assert(x >= 0) {
print('In Point.withAssert(): ($x, $y)');
}
初始化final字段時,初始化列表很方便。下面的示例在初始化列表中初始化三個final字段。
import 'dart:math';
class Point {
final num x;
final num y;
final num distanceFromOrigin;
Point(x, y)
: x = x,
y = y,
distanceFromOrigin = sqrt(x * x + y * y);
}
main() {
var p = new Point(2, 3);
print(p.distanceFromOrigin);
}
(5)常量構造函數
如果您的類產生了永不改變的對象,則可以使這些對象具有編譯時常量。爲此,請定義一個const構造函數,並確保所有實例變量均爲final。
class ImmutablePoint {
static final ImmutablePoint origin =
const ImmutablePoint(0, 0);
final num x, y;
const ImmutablePoint(this.x, this.y);
}
flutter中Image.asset源碼示例:
Image.asset(
String name, {
Key key,
AssetBundle bundle,
this.frameBuilder,
this.semanticLabel,
this.excludeFromSemantics = false,
double scale,
this.width,
this.height,
this.color,
this.colorBlendMode,
this.fit,
this.alignment = Alignment.center,
this.repeat = ImageRepeat.noRepeat,
this.centerSlice,
this.matchTextDirection = false,
this.gaplessPlayback = false,
String package,
this.filterQuality = FilterQuality.low,
}) : image = scale != null
? ExactAssetImage(name, bundle: bundle, scale: scale, package: package)
: AssetImage(name, bundle: bundle, package: package),
loadingBuilder = null,
assert(alignment != null),
assert(repeat != null),
assert(matchTextDirection != null),
super(key: key);