Dart語言一日遊
前言
Dart是谷歌Flutter框架使用的語言。Dart語言目前還是個冷門語言,但是它很好上手,對於有程式設計基礎的人來說,一天時間足夠掌握了。本文就是結合我自己的學習過程,給大家總結一下經驗,幫助大家快速上手。
學習Dart語言,這一篇文章就夠用了。
本文包括哪些知識點
- 如何建立一個類和它的構造器
- 如何定義類的變數
- 如何建立getter和setter
- Dart對於變數私有公有訪問的控制
- 如何使用工廠模式建立一個類例項
- 類的繼承機制
- Dart函數語言程式設計
需要準備的東西
因為本文只是學習Dart語言,只需要一個能科學上網的瀏覽器就夠用了。
Dart提供了線上的IDE開發環境DartPad
,極大的方便了我們的學習。
萬物之源:類和物件
對於一門物件導向的程式語言,類和物件永遠都是最重要的組成部分。所以我們首先來看如何建立一個Dart類。
開啟DartPad
,發現已經幫你寫好了一個HelloWorld程式了。其中main函式就是入口主函式。這個函式迴圈輸出四行字串。
void main() {
for (var i = 0; i < 4; i++) {
print('hello $i');
}
}
複製程式碼
類的建立
下面我們來建立一個類,我們用自行車來距離。建立一個Bicycle類。程式碼如下:
class Bicycle{
int speed;
String desc;
}
void main() {
}
複製程式碼
怎麼樣,是不是非常熟悉?(注意,這裡每行結尾還是要寫分號,略蛋疼)
這個類有兩個屬性,速度和描述。如何建立一個例項呢?也很簡單,這樣就好了。
class Bicycle{
int speed;
String desc;
}
void main() {
var bike = Bicycle();
bike.speed = 20;
bike.desc = "我的自行車";
print(bike);
}
複製程式碼
點一下頁面上的run按鈕,輸出結果:
Instance of 'Bicycle'
複製程式碼
這裡有兩個知識點:
- 我們用
var
關鍵字建立了一個bike例項並且輸出。如果想要建立一個不可變的變數,用關鍵字final
。(這個final就是kotlin的val) - 類的屬性都沒有
public
或者private
關鍵字。沒錯,Dart沒有public
和private
關鍵字,類裡面宣告的屬性預設都是公共的。這個要注意一下。如何宣告私有變數後面再講。
建構函式
能不能像我們習慣的那樣寫個建構函式傳參呢?當然可以,這樣寫就行了:
class Bicycle {
int speed;
String desc;
Bicycle(this.speed, this.desc);
}
void main() {
var bike = Bicycle(20, "我的自行車");
print(bike);
}
// 上面的簡便建構函式寫法與下面的等價:
Bicycle(int speed, String desc) {
this.speed = speed;
this.desc = desc;
}
複製程式碼
run一下,輸出和剛才還是一樣的
Instance of 'Bicycle'
複製程式碼
這個輸出不太友好,我們來改進一下。同Java一樣,每個類有自己的toString()
函式,我們覆寫它就可以。
class Bicycle {
int speed;
String desc;
Bicycle(this.speed, this.desc);
@override
String toString() {
return '$desc速度: $speed 公里/小時';
}
// 簡便寫法
@override
String toString() => '$desc速度: $speed 公里/小時';
}
複製程式碼
注意,函式前面不用寫fun
關鍵字。簡便的寫法中,用=>
符號來確定返回值。最後的輸出為:
我的自行車速度: 20 公里/小時
複製程式碼
好了下面我們來介紹一下如何宣告一個私有的變數。因為沒有private
關鍵字,所以Dart用了一個比較蛋疼的處理,就是變數名前面加下劃線,你沒看錯,是下劃線。。。所以我們把desc
變數宣告為_desc
,這樣就把它私有化了。
class Bicycle {
int speed;
String _desc;
Bicycle(int speed) {
this.speed = speed;
this._desc = "我的自行車";
}
@override
String toString() {
return '$_desc速度: $speed 公里/小時';
}
}
void main() {
var bike = Bicycle(20);
print(bike);
}
複製程式碼
這樣修改後,在main函式中就無法訪問desc變數了,不能set也不能get。
那麼問題又來了,如果我想建立一個只讀的變數呢?能get但是不能set。這個就需要在私有變數的基礎上,自己提供一個get函式了。寫法是這樣的
class Bicycle {
int speed;
String _desc;
// 為私有變數提供get函式
String get desc => _desc;
Bicycle(int speed) {
this.speed = speed;
this._desc = "我的自行車";
}
@override
String toString() {
return '$_desc速度: $speed 公里/小時';
}
}
void main() {
var bike = Bicycle(20);
print(bike.desc);
print(bike);
}
複製程式碼
最後介紹一下為一個類新增函式,這個非常簡單,看一下就好:
class Bicycle {
int speed;
String _desc;
void speedUp(int value) {
this.speed += value;
}
int getSpeed() {
return this.speed;
}
}
複製程式碼
OK,類和物件這個部分已經完成了。
可選引數
Dart和Kotlin一樣,支援函式設定可選引數,避免Java那種累贅的過載。這裡我們用這個例子RectangleExample
為這個類加入一個可選引數的建構函式,最後再加一個toSting。
class Rectangle {
int width;
int height;
Point origin;
Rectangle({Point origin = const Point(0, 0), int width, int height}) {
this.origin = origin;
this.width = width;
this.height = height;
}
@override
String toString() =>
'Origin: (${origin.x}, ${origin.y}), width: $width, height: $height';
}
複製程式碼
這裡座標orgin設定了預設引數。注意,使用預設引數時,需要將引數包裹在{ }中。之後再main函式裡進行呼叫:
void main() {
print(Rectangle(origin: const Point(10, 20), width: 100, height: 200));
print(Rectangle(origin: const Point(10, 10)));
print(Rectangle(width: 200));
print(Rectangle());
// 注意:這樣寫會報錯,需要加引數名
print(Rectangle(100, 100));
}
複製程式碼
注意,這裡需要填寫引數名稱,不能直接給值。這一點上相比於Kotlin確實要麻煩一些。
最後控制檯的輸出是這樣的:
Origin: (10, 20), width: 100, height: 200
Origin: (10, 10), width: null, height: null
Origin: (0, 0), width: 200, height: null
Origin: (0, 0), width: null, height: null
複製程式碼
使用工廠模式
使用工廠模式建立類的例項是開發中常用的情況。Dart對此還特意提供了一個factory
關鍵字。下面我們來介紹一下如何在Dart中快速實現工廠模式。先看這個例子:
import 'dart:math';
abstract class Shape {
num get area;
}
class Circle implements Shape {
final num radius;
Circle(this.radius);
num get area => pi * pow(radius, 2);
}
class Square implements Shape {
final num side;
Square(this.side);
num get area => pow(side, 2);
}
複製程式碼
這裡有幾個知識點:
- 第一行的import,說明Dart提供了數學運算等一系列基礎庫,例如
dart:core
,dart:async
,dart:convert
,dart:collection
,這個後面可以自己去看API詳細瞭解 - 和Kotlin一樣,一個檔案可以定義多個類
- Dart也是支援抽象類的,繼承的時候,使用
implements
關鍵字。Dart中沒有interface介面的概念,這個後面再細講。
下面針對父類Shape
加入一個工廠建構函式
,使用關鍵字factory
,並且在main函式中呼叫,程式碼如下:
abstract class Shape {
num get area;
factory Shape(String type) {
if (type == 'circle') return Circle(2);
if (type == 'square') return Square(2);
throw 'Can\'t create $type.';
}
}
void main() {
try {
print(Shape('circle').area);
print(Shape('square').area);
print(Shape('triangle').area);
} catch (err) {
print(err);
}
}
複製程式碼
輸出如下:
12.566370614359172
4
Can't create triangle.
複製程式碼
觀察這段程式碼,裡面引入了異常處理機制。使用工廠函式建立的時候,如果傳入的型別不合法,會throw
一個異常。然後在main函式中使用try/catch
函式來捕獲這個異常並處理。另外,因為示例程式碼中用單引號包裹字串,所以異常文案中的Can't creat
用到了轉義字元,用雙引號包裹字串也是可以的。
當然除了上面介紹的使用factory
關鍵字建立工廠建構函式之外,你也可以像Kotlin一樣,自己寫一個工廠函式,例如:
Shape shapeFactory(String type) {
if (type == 'circle') return Circle(2);
if (type == 'square') return Square(2);
throw 'Can\'t create $type.';
}
複製程式碼
這個效果是一樣的,覺得那種順手就用哪一種吧。 示例程式碼在這裡
Interface介面問題
前面說了,Dart沒有Java裡面Interface的概念。為什麼呢?我來告訴你為什麼?你說為什麼?!
額,抱歉,寫了這長了,偶爾皮一下。
實際上是因為每一個Dart類都隱形的有一個介面。到底什麼意思呢?來看看剛才的例子
在其中加入一個CircleImpl類繼承Circle:
class CircleImpl implements Circle {}
複製程式碼
執行發現會報錯:
Error: The non-abstract class 'CircleImpl' is missing implementations for these members:
- 'radius'
- 'area'
複製程式碼
這是因為Circle中含有radius和area兩個屬性,雖然Circle類中已經實現了這兩個引數的處理,但是CircleImpl類並沒有繼承到具體實現。
所以Dart的繼承並不是真正意義上的繼承那個類,而是繼承這個類對應的資料結構的介面,這個資料結構包括它的引數和函式,但是不包括實現。這也是為什麼Dart繼承的關鍵字是implement
而不是extends
CircleImpl類繼承的不是Circle,只是繼承了它的資料結構對應的介面,只是告訴你需要有radius和area兩個屬性,但是沒有把實現繼承給你。
所以我們把CircleImpl
類改成這樣就好了:
class CircleImpl implements Circle {
num radius;
num area;
}
複製程式碼
可以自己嘗試寫一個帶函式的類,然後寫個子類繼承它,看看是什麼效果?
函數語言程式設計
函數語言程式設計對於熟練使用Kotlin和Swift的人應該都不陌生了,Dart也是有相應的特性的。
下面我們來介紹一下,首先是函式的返回可以直接是一個表示式,這個前面都見過:
// 函式式風格寫法
String sayHello() => "Hello World";
// 等價於傳統返回值寫法
String sayHello() {
return "Hello World";
}
複製程式碼
再來看看對映操作、集合遍歷和將函式作為引數的特性:
String sayHello(int time) => "Hello World! $time";
main() {
final values = [1, 2, 3, 4, 5, 6];
values.map(sayHello).forEach(print);
}
// 輸出結果
Hello World! 1
Hello World! 2
Hello World! 3
Hello World! 4
Hello World! 5
Hello World! 6
複製程式碼
這裡首先用到了map對映,values是一個int陣列,用map對映,將int通過函式sayHello變成了一個字串陣列。之後再用集合遍歷forEach,遍歷的呼叫print函式列印結果。這裡map和forEach都是將一個函式當做引數接收了。
此外Dart集合框架中支援map和set資料結構,而且提供了類似Kotlin的那些便捷函式,例如下面這個寫法,就是跳過第一個元素,之後再取前三個元素進行處理:
main() {
final values = [1, 2, 3, 4, 5, 6];
values.skip(1).take(3).map(sayHello).forEach(print);
}
// 輸出結果
Hello World! 2
Hello World! 3
Hello World! 4
複製程式碼
最後說說控制流,為什麼寫到最後還不講講控制流呢,因為Dart的控制流和Java真的一模一樣,什麼if/else啊,switch啊都一樣,除了沒有when語句。這個隨便寫寫就知道了。
寫在最後
恭喜你已經入坑了。讀到這裡你已經掌握了Dart語言的全部基礎知識!怎麼樣不難吧,雖然Dart冷門了點,但是技多不壓身嘛。
詳細的類庫API可以查閱Dart語言官網
。這裡寫的非常詳細。就像當年剛學Java的時候看JavaAPI一樣使用它就行了。
後面我會再寫寫Flutter的遊玩感受。這個東西我個人認為目前就是調研調研,簡單瞭解一下就好,沒必要深入學習。其實Dart語言看完這篇文章就夠用了,太細節的類庫API用到的時候查一下就好了。