運算子(Operators)
四則運算、邏輯運算、程式運算等比較基礎的運算部份請參看官方文檔。
在此講一些比較特別的運算子
as, is, is!
// Type test operators
num n = 1.1;
print(n is! int);
print(n is double);
true
true
條件算符(Conditional expressions)
大部分程式語言都會有三元運算子 ?:
,它可以很簡短的代替 if-else
語法。
Dart 中有幾個比較有趣的寫法 ??
、?.
等。
??
與 ?.
都會判斷左方的運算元是否為 null
,再進行動作。
此範例若有些看不懂,可以看完下面一節 類別(上) 後再回頭看一次這裡。
class Account {
String username;
String password;
Account({username, password})
: this.username = username,
// 下面兩種寫法是等價的
// this.password = (password != null) ? password : 'AutomaticallyGenerateSecretPassword';
this.password = password ?? 'AutomaticallyGenerateSecretPassword';
@override
String toString() {
return "Account($username, $password)";
}
}
var a = Account(username: 'Bobson');
print(a);
var b = Account();
b?.username = "I'm Groot";
print(b);
Account(Bobson, AutomaticallyGenerateSecretPassword)
Account(I'm Groot, AutomaticallyGenerateSecretPassword)
級聯符號(Cascade notation)
級聯符號可讓你針對一個物件進行一連串的函數操作,可省下寫重複變數的動作。
不用級聯符號的寫法:
var list = List()
list.insert(0, 123)
list.add(456)
list.addAll([789, 111]);
用級聯符號的寫法:
var list = List()
..insert(0, 123)
..add(456)
..addAll([789, 111]);
print(list);
[123, 456, 789, 111]
類別(Class) - 上篇
物件導向程式(OOP),是軟體程式設計中很重要的一環,打算用較長篇幅講解。
物件導向簡單說,從大到小;從整體應用(業務)邏輯到程式組件,都為他們繪製藍圖(也就是類別
),
而電腦在執行程式時,會依照藍圖製造出成品(也就是物件
或實例
),中間的製造過程統稱實例化
。
直接來看些 Dart 中 OOP 的應用吧。
// Point Class from dart:math
var p1 = Point(1, 2);
var p2 = Point(3, 4);
print("p1: $p1, p2: $p2");
print(p1.distanceTo(p2));
print(p2.magnitude);
p1: Point(1, 2), p2: Point(3, 4)
2.8284271247461903
5.0
Dart 中的 OOP 與其他 OOP 語言特性並無太多不同,
我找了 Dart 的數學函式庫 dart:math
中的 Point
(二維平面上的點) 作為開篇。
class Point<T extends num> {
final T x;
final T y;
const Point(T x, T y)
: this.x = x,
this.y = y;
String toString() => 'Point($x, $y)';
...
/**
* Get the straight line (Euclidean) distance between the origin (0, 0) and
* this point.
*/
double get magnitude => sqrt(x * x + y * y);
/**
* Returns the distance between `this` and [other].
*/
double distanceTo(Point<T> other) {
var dx = x - other.x;
var dy = y - other.y;
return sqrt(dx * dx + dy * dy);
}
...
}
類別通常都會包含幾個東西:
- 屬性(property) 或 成員(member) - 物件會用到的變數
- 建構子(constructor) - 可理解成創建(初始化)物件時會調用的函數
- 方法(methods) - 物件會用到的函數
以 Point
對應來說:
- 屬性或成員 -
final T x
(此點的 x 座標)final T y
(此點的 y 座標)
- 建構子 -
const Point(T x, T y)...
- 方法 -
String toString()...
(print 時所顯示的字串),double get magnitude...
(此點與原點的距離),double distanceTo(Point<T> other)...
(此點與另一點的距離)
對應完之後是不是覺得很好理解了呢,這也是 OOP 的迷人之處。
接下來,我們試看看用 Dart 實現數學中複數(complex)的類別吧。
Complex Number (複數) - x + yi (i = √-1) 其中 x 及 y 皆為實數,分別稱為複數之「實部」和「虛部」。
class BaseComplex {
num real;
num imaginary;
@override
bool operator ==(dynamic other) {
if (other is! BaseComplex) {
return false;
} else {
return real == other.real && imaginary == other.imaginary;
}
}
@override
String toString() => "BaseComplex($real, $imaginary)";
}
- 屬性或成員 -
num real
(實部)num imaginary
(虛部)
- 建構子 - 無
- 方法 -
bool operator ==(dynamic other)...
(複數物件相等函數)String toString()...
(print 時所顯示的字串),
稍稍分解一下,有兩項額外重點:
- 建構子不寫的話會有一個預設的建構子可以調用,別忘了 Dart 中一次都繼承於
Object
,所以實際上預設是調用了Object
的建構子。 @override
這裝飾子是用來覆寫父類別的方法,Object
中包含了==()
和toString()
。
接著實際建造 BaseComplex
看看吧~
var c1 = BaseComplex();
c1.real = 5;
c1.imaginary = 2;
print(c1.runtimeType);
var c2 = BaseComplex()
..real = 5
..imaginary = 2;
print("c1: $c1, c2: $c2");
print(c1 == c2);
BaseComplex
c1: BaseComplex(5, 2), c2: BaseComplex(5, 2)
true
runtimeType
可顯示程式執行時,此變數的型別是什麼print("c1: $c1, c2: $c2");
print 時會調用到物件的toString
函數,若無覆寫toString
會顯示Instance of 'BaseComplex'
。c1 == c2
中實際上會調用我們覆寫的==
函數。
稍微有些基礎建立類別方法了吧~
要多加一些特性在實際 Complex
物件囉。
class Complex {
// Private property(member)
num _real;
num _imaginary;
// Getters & Setters
get real => _real;
set real(num newReal) => _real = newReal;
get imaginary => _imaginary;
set imaginary(num newImaginary) => _imaginary = newImaginary;
// Traditional way
// Complex(num real, num imaginary) {
// this.real = real;
// this.imaginary = imaginary;
// }
// Syntactic sugar
Complex(this._real, this._imaginary);
// Named Constructor
Complex.real(num real) : this(real, 0);
Complex.imaginary(num imaginary) : this(0, imaginary);
@override
bool operator ==(dynamic other) {
if (other is! Complex) {
return false;
} else {
return real == other.real && imaginary == other.imaginary;
}
}
@override
String toString() {
if (imaginary >= 0) {
return "$real + ${imaginary}i";
} else {
return "$real - ${imaginary.abs()}i";
}
}
}
這裡整理一下,加了哪些東西?
- Private property(member) - (相當於 Java 裡的
private
)- Dart 中的類別屬性(成員)前面加
_
,可達到屬性被保護的作用。 - 建造出來的物件 private 屬性無法直接透過
.
獲取。
- Dart 中的類別屬性(成員)前面加
- Getters & Setters - (相當於
JavaBeans
)- 屬於類別中的方法(methods),想要類別中的屬性有讀與寫的權限,可以讓屬性變 private,並寫個自定義的 Get 函數(讀)、Set 函數(寫)。
- 但在物件操作上如此(都用自定義Get 函數和 Set 函數)會變得不方便,所以可直接使用
get
和set
,在 Dart 中針對他們有對應的 keyword 可用。
- 建構子
- 傳統一般的建構子會用大括號來定義其初始的內容。
- 在 Dart 中有更精簡的寫法(Syntactic sugar 語法糖)。
- 命名建構子(Named Constructor)
- 有時類別內會有特別常用的特定型態,可以額外寫成命名建構子。
- 在 Flutter 中可以常常看到這種應用,如
ListView
中就有好幾個命名建構子ListView.builder
、ListView.seperated
、ListView.custom
。
- 初始化列表(Initializer list)
- 在建構子建構物件之前,可以用
:
來設定一些初始值。 - 在 Flutter 源碼中可以常常看到這種應用。
- 在建構子建構物件之前,可以用
你也可以瞧瞧…
參考
- Dart 官方文檔 - Operators
- Dart 官方文檔 - Classes
- Introduction to Dart for Beginners - Intro to Classes and Objects - Part Three
系列文