類別(Class) - 中篇
延續上一節的 類別(Class) - 上篇
方法(Methods)
Dart 中有幾種類別中的方法:
- 實例方法(Instance methods) - 就是一般類別中標準的函數,使用的時候要先以類別實例化出物件,該物件才能使用此方法。
- 靜態方法(Static methods) - 跟一般類別中的函數很像,不過它不用實例化便可使用該方法,通常是與該類別相關所以會定義在類別裡。
- 抽象方法(Abstract methods) - 抽象類別中的函數,用來定義樣板(或稱界面[interface]),繼承該抽象類別後所需實現(implement)的函數。
- Getters & Setters - 範例中
Complex
的real
和imaginary
- 運算子方法(Operator methods) - Dart 中可以 Override 幾個常用的運算子的方法,如
+ - * /
> < ==
… 等等。
!=
是不可複寫的,因為它相當於!(a == b)
我們在 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;
// Constructors
Complex(this._real, this._imaginary);
Complex.real(num real) : this(real, 0);
Complex.imaginary(num imaginary) : this(0, imaginary);
Complex multiply(Complex c) {
return Complex(
(this.real * c.real) - (this.imaginary * c.imaginary),
(this.real * c.imaginary) + (this.imaginary * c.real),
);
}
static Complex substract(Complex c1, Complex c2) {
return Complex(
c1.real - c2.real,
c1.imaginary - c2.imaginary,
);
}
Complex operator +(Complex c) {
return Complex(this.real + c.real, this.imaginary + c.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";
}
}
}
- 實例方法(Instance methods) -
Complex.multiply
- 靜態方法(Static methods) -
Complex.substract
- 運算子方法(Operator methods) -
Complex.+
按照邏輯,數學上的運算覆寫
運算子方法(Operator methods)
會比較合理。
這邊展示用所以才拆開;而抽象方法(Abstract methods)
使用情況較特別,後面會在講述。
使用看看這些方法吧
var n1 = Complex(3, -4);
var n2 = Complex(5, 2);
print(n1 + n2);
print(n1.multiply(n2));
print(Complex.substract(n1, n2));
8 - 2i
23 - 14i
-2 - 6i
final & const
final
與 const
變數在 Dart 中是常見的,在大部份的情況下他們的結果是相同的,就是不能任意更改賦予此變數的值。
但實際意義上是有些不同的,final
是指第一次賦予此變數值之後就不能作改變了,也是就 final
的作用是發生於 Run-time 的,也就是程式執行當下決定;
而 const
是在 Compile-time 就決定的,所以在程式編譯後就已經決定好了,程式執行當下也不能作改變。
在物件導向(OO)的類別中,常用 final
在類別的屬性,目的在於創建物件(實例)後希望該屬性不再改變,比如在 Complex
得屬性中加上 final
。
但後續 real
和 imaginary
Setter 方法就都不能使用,會出現 '_real' can't be used as a setter because it is final.
的錯誤。
class Complex {
// Private property(member)
final num _real;
final num _imaginary;
get real => _real;
set real(num newReal) => _real = newReal; // 會出現 Error.
get imaginary => _imaginary;
set imaginary(num newImaginary) => _imaginary = newImaginary; // 會出現 Error.
...
}
而 const
在物件中,需寫成 static const
通常都是常用且不變的類別屬性,比如 dart:math
中的數學常數、package:http/http.dart
中的 HttpHeaders
。
// dart:math
/**
* Base of the natural logarithms.
*
* Typically written as "e".
*/
const double e = 2.718281828459045;
...
/**
* The PI constant.
*/
const double pi = 3.1415926535897932;
...
// package:http/http.dart
abstract class HttpHeaders {
static const acceptHeader = "accept";
static const acceptCharsetHeader = "accept-charset";
static const acceptEncodingHeader = "accept-encoding";
static const acceptLanguageHeader = "accept-language";
static const acceptRangesHeader = "accept-ranges";
static const ageHeader = "age";
static const allowHeader = "allow";
static const authorizationHeader = "authorization";
...
}
繼承(Inheritance) - 擴展(extends)
物件導向(Object-Oriented)中的繼承,是一個非常核心的概念,可細分非常多種。
要理解很多種概念(或是設計模式),才有辦法做出更好程式設計。
這邊主要講的是 extends
(擴展、延伸)。
標準的寫法 class 子類 extends 父類(基類)
。
這樣的用法主要是子類必須包含父類的所有特性,並且有自己的額外特性。此用法可以達成程式碼重用(reuse)效果。
注意: Dart 中的
extends
只支援單一繼承。
Dart 簡單一點的繼承應用,可以看到 num
、int
和 double
的繼承關係。
abstract class double extends num {
static const double nan = 0.0 / 0.0;
static const double infinity = 1.0 / 0.0;
static const double negativeInfinity = -infinity;
static const double minPositive = 5e-324;
static const double maxFinite = 1.7976931348623157e+308;
double remainder(num other);
...
}
abstract class int extends num {
...
int gcd(int other);
/** Returns true if and only if this integer is even. */
bool get isEven;
/** Returns true if and only if this integer is odd. */
bool get isOdd;
...
}
- 除了
num
的基本特性,int
和double
多了自己的特性。
複雜一點,可以看 Flutter 中的 ListView
和 BoxScrollView
的繼承關係。
class ListView extends BoxScrollView {
ListView({
Key key,
Axis scrollDirection = Axis.vertical,
bool reverse = false,
ScrollController controller,
bool primary,
ScrollPhysics physics,
bool shrinkWrap = false,
EdgeInsetsGeometry padding,
this.itemExtent,
bool addAutomaticKeepAlives = true,
bool addRepaintBoundaries = true,
bool addSemanticIndexes = true,
double cacheExtent,
List<Widget> children = const <Widget>[],
int semanticChildCount,
}) : childrenDelegate = SliverChildListDelegate(
children,
addAutomaticKeepAlives: addAutomaticKeepAlives,
addRepaintBoundaries: addRepaintBoundaries,
addSemanticIndexes: addSemanticIndexes,
), super(
key: key,
scrollDirection: scrollDirection,
reverse: reverse,
controller: controller,
primary: primary,
physics: physics,
shrinkWrap: shrinkWrap,
padding: padding,
cacheExtent: cacheExtent,
semanticChildCount: semanticChildCount ?? children.length,
);
...
final double itemExtent;
final SliverChildDelegate childrenDelegate;
@override
Widget buildChildLayout(BuildContext context) {
if (itemExtent != null) {
return SliverFixedExtentList(
delegate: childrenDelegate,
itemExtent: itemExtent,
);
}
return SliverList(delegate: childrenDelegate);
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
super.debugFillProperties(properties);
properties.add(DoubleProperty('itemExtent', itemExtent, defaultValue: null));
}
// Helper method to compute the semantic child count for the separated constructor.
static int _computeSemanticChildCount(int itemCount) {
return math.max(0, itemCount * 2 - 1);
}
}
ListView
的建構子中的初始列表,把相當多的特性都用super
傳給了父類BoxScrollView
。ListView
擁有自己的特性,如屬性itemExtent
、childrenDelegate
;方法_computeSemanticChildCount
。ListView
也覆寫了父類的特性,如buildChildLayout
和debugFillProperties
。
物件導向中的繼承是個概念簡單,但實際上使用可以相當複雜。
需要多看多寫程式碼才能更加深入了解這概念的博大精深。
重構
也是程序員常做事情,畢竟人也是會慢慢進步,會發現以前寫的程式碼的一些缺陷,進而改進。
你也可以瞧瞧…
參考
- Dart 官方文檔 - Final and const
- Dart 官方文檔 - Classes
- Introduction to Dart for Beginners - Methods, Final, Static and Basic Class Inheritance - Part Four
系列文