級別: ★☆☆☆☆
標簽:「Flutter 」「Dart」「Dart class」「Dart mixin」「Dart override」
作者: WYW
審校: QiShare團隊
前言:
4篇Dart基礎已經全部更新完成。目錄如下:
Dart 基礎 (一)
Dart 基礎 (二)
Dart 基礎 (三)
Dart 基礎 (四)
筆者在本文中主要會分享:類、實例變量、構造方法、命名構造方法、實例方法、靜態變量、靜態方法、set、get 方法、extends
、implements
、mixin
、abstract
、override
相關的內容。
1.類
下邊筆者先以Point 類為例,分享下關于實例變量
,構造方法
,命名構造方法
,實例方法
、靜態方法
、 靜態變量
、set get方法
的內容。
Dart 是一種面向對象的編程語言,同時支持基于 mixin 的繼承機制。mixin相關的內容會在下文解釋。每個對象都是一個類的實例,所有的類都繼承于 Object。 基于 Mixin 的繼承 意味著每個類(Object 除外) 都只有一個超類,一個類的代碼可以在其他 多個類繼承中重復使用。
使用 new 關鍵字和構造方法來創建新的對象。 構造方法名字可以為 ClassName 或者 ClassName.identifier。
在Dart2.0的時候,創建新的對象的時候,new 關鍵字是可選的。當前Dart最新版本是2.4.0,2019-06-27 Dart開發團隊發布2.4.0版本Dart。 Dart change log
1.1 實例變量
class Point {
// 實例變量
num x;
num y;
}
1.2 構造方法:構造方法 定義一個和類名一樣的方法
// 構造方法 定義一個和類名一樣的方法
Point(num x, num y) {
// this 關鍵字指當前的實例
this.x = x;
this.y = y;
}
// 由于把構造方法參數賦值給實例變量的場景太常見了, Dart 提供了一個語法糖來簡化這個操作
// Point(this.x, this.y);
1.3 命名構造方法
// 命名構造方法
Point.fromJson(Map json) {
// 只有當名字沖突的時候才使用 this。否則的話, Dart 代碼風格樣式推薦忽略 this。
x = json['x'];
y = json['y'];
}
Point.namedConstructor(Map json){
x = json['x'];
y = json['y'];
}
命名構造方法使用場景有:模型類中解析數據場景。
舉個簡單例子:如返回一個列表數據的情況,返回數據可能是是一個包著多個字典的數組,那么,處理相應數據的時候,需要對數據進行相應的解析。解析的過程就可能用到命名構造方法。把一個個字典當做實例,提取出來。
[
{
"name":"QiShare1",
"age":"1"
},
{
"name":"QiShare2",
"age":"1"
},
{
"name":"QiShare3",
"age":"1"
},
{
"name":"QiShare4",
"age":"1"
},
{
"name":"QiShare5",
"age":"1"
},
{
"name":"QiShare6",
"age":"1"
},
{
"name":"QiShare7",
"age":"1"
},
]
1.4 實例方法
// 實例方法
num distanceTo(Point other) {
var dx = x - other.x;
var dy = y - other.y;
return sqrt(dx*dx + dy*dy);
}
1.5 靜態方法
使用static關鍵字修飾的方法為靜態方法,相當于類方法。使用類名可以直接調用。
// 靜態方法
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 classDemo() {
var jsonData = jsonDecode('{"x":2, "y":2}');
// Create a Point using Point().
var p1 = new Point(1, 1);
print('p1點x坐標:${p1.x}');
print('p1點y坐標:${p1.y}');
// Create a Point using Point.fromJson().
var p2 = new Point.fromJson(jsonData);
print('p2點x坐標:${p2.x}');
print('p2點y坐標:${p2.y}');
num distance = p2.distanceTo(p1);
print('p1到p2的距離: $distance');
Map jsonData3 = {
'x': 3,
'y': 3,
};
Point p3 = Point.namedConstructor(jsonData3);
print('p3點x坐標:${p3.x}');
print('p3點y坐標:${p3.y}');
num distance12 = Point.distanceBetween(p1, p2);
print('p1和p2之間的距離 $distance12');
}
輸出內容
flutter: p1點x坐標:1
flutter: p1點y坐標:1
flutter: p2點x坐標:2
flutter: p2點y坐標:2
flutter: p1到p2的距離: 1.4142135623730951
flutter: p3點x坐標:3
flutter: p3點y坐標:3
flutter: p1和p2之間的距離 1.4142135623730951
1.6 靜態變量
靜態變量對于類級別的狀態是非常有用的,筆者對這句話的理解是:靜態變量可以由類名直接調用。
class Color {
static const red =
const Color('red'); // A constant static variable.
final String name; // An instance variable.
const Color(this.name); // A constant constructor.
}
使用方式
String colorName = Color.red.name;
print('colorName:$colorName');
輸出內容
colorName:red`
1.7 set get 方法
下邊筆者舉了一個類Rectangle的left、top、width、height的Set、Get方法的例子。
class Rectangle {
num left;
num top;
num width;
num height;
Rectangle(this.left, this.top, this.width, this.height);
// Define two calculated properties: right and 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;
}
使用方式
Rectangle rectangel = Rectangle(0, 0, 375, 667);
print('rectangel.left:');
print(rectangel.left);
print('rectangel.right:');
print(rectangel.right);
print('rectangel.width:');
print(rectangel.width);
print('rectangel.height:');
print(rectangel.height);
print('rectangel.right:');
print(rectangel.right);
print('rectangel.bottom:');
print(rectangel.bottom);
輸出結果:
flutter: rectangel.left:
flutter: 0
flutter: rectangel.right:
flutter: 375
flutter: rectangel.width:
flutter: 375
flutter: rectangel.height:
flutter: 667
flutter: rectangel.right:
flutter: 375
flutter: rectangel.bottom:
flutter: 667
2. extends 與 implements
extends
關鍵字extends 用于繼承父類的實例變量及方法等。Dart 只支持單繼承。
implements
Every class implicitly defines an interface containing all the instance members of the class and of any interfaces it implements. If you want to create a class A that supports class B’s API without inheriting B’s implementation, class A should implement the B interface.
每個類都隱式地聲明了一個包含所有的實例變量和類已經實現的接口。
如果你想創建一個類A,沒有繼承類B,但是類A可訪問類B的API,那么類A 應該實現類B的接口。
上邊的內容,結合著下邊的例子,筆者的理解是:Chicken隱式地聲明了Animal 的實例變量,和類Animal 已經實現的方法。Chicken支持在沒有繼承類Animal的情況下,可訪問類B的API。
一個類可以implements 多個類的API,所以implements算是一種變向實現多繼承的方式。
class Animal {
String name;
void ability() {
print('Animal 的能力');
}
}
class Bird extends Animal {
void ability(){
print('bird can fly');
}
}
class Fish extends Animal {
void ability(){
print('fish can swim');
}
}
class Dog extends Animal {
void ability(){
print('dog can bark');
}
}
class Chicken implements Animal {
String name;
void ability() {
print('chicken can lay eggs');
}
}
調用如上代碼的方式及相應輸出結果如下:
Dog dog = Dog();
dog.ability();
Fish fish = Fish();
fish.ability();
Bird bird = Bird();
bird.ability();
Chicken chicken = Chicken();
chicken.ability();
// 輸出結果:
flutter: dog can bark
flutter: fish can swim
flutter: bird can fly
flutter: chicken can lay eggs
3. mixin
Mixins 是一種在多類繼承中重用一個類代碼的方法。筆者的理解是,mixin相當于是一個工具類,使用
with
關鍵字使用了mixin的類,就可以使用mixin中的代碼。
Mixins are a way of reusing a class’s code in multiple class hierarchies.
To use a mixin, use the with keyword followed by one or more mixin names. The following example shows two classes that use mixins:
Mixin 是一種在多個類中重用某些代碼的方式。
使用mixin ,需使用with
關鍵字,with后邊跟mixin的名,with 后邊可以跟多個mixin名字,及可以同時使用多個mixin中的代碼。下邊筆者舉了一個開發者學習基礎語言的例子。筆者定義了一個Developer的mixin,如果是iOS 開發者需要先學習C語言基礎,如果是Android 開發者,需要先學習Java語言,如果是Flutter 開發者,需要先學習Dart 語言。
mixin Developer {
bool isIOS = false;
bool isAndroid = false;
bool isFlutter = false;
// 需要學習的基礎語言
void needLearnBaseProgram () {
if (isIOS) {
print('Need Learn C Firstly');
} else if (isAndroid) {
print('Need Learn Java Firstly');
} else if (isFlutter) {
print('Need Learn Dart Firstly');
} else {
print('May be need Learn Other Language');
}
}
}
class FlutterDeveloper with Developer {
String name;
FlutterDeveloper(String name) {
isFlutter = true;
this.name = name;
}
}
使用的相關代碼:
FlutterDeveloper flutterDeveloper = FlutterDeveloper('FlutterEnginerName');
flutterDeveloper.needLearnBaseProgram();
// 輸出結果: flutter: Need Learn Dart Firstly
注意事項: 當在if else 場景下使用 bool 類型變量的時候,需要注意bool變量是否賦值過了,否則會有類似如下的異常信息。
flutter: The following assertion was thrown while handling a gesture:
flutter: Failed assertion: boolean expression must not be null
4. abstract
使用 abstract 修飾的類 記為抽象類。抽象類用于定義接口 及部分實現。
筆者舉了如下例子:
創建了People 類,并且聲明了 String skinColor();的抽象方法,創建并實現了 void ability() 方法;
abstract class People {
String skinColor();
void ability() {
print('All can Communicate');
}
}
class YellowPeople extends People {
@override
String skinColor() {
String color = 'Yellow';
print(color);
return color;
}
}
class BlackPeople extends People {
@override
skinColor() {
String color = 'black';
print(color);
return color;
}
}
class WhitePeople extends People {
@override
skinColor() {
String color = 'White';
print(color);
return color;
}
}
下邊是使用示例,及相應的輸出結果。
YellowPeople yellowPeople = YellowPeople();
yellowPeople.ability();
yellowPeople.skinColor();
WhitePeople whitePeople = WhitePeople();
whitePeople.ability();
whitePeople.skinColor();
BlackPeople blackPeople = BlackPeople();
blackPeople.ability();
blackPeople.skinColor();
// 輸出結果:
flutter: All can Communicate
flutter: Yellow
flutter: All can Communicate
flutter: White
flutter: All can Communicate
flutter: black
- 抽象類不能創建實例。
- 抽象方法為沒有方法體的方法。只有抽象類中可以寫抽象方法,其他普通類不可以。
- 例:如果BlackPeople的skinColor 沒有方法體即沒有實現,則會報錯如下:'skinColor' must have a method body because 'BlackPeople' isn't abstract.
Try making 'BlackPeople' abstract, or adding a body to 'skinColor'.
- 例:如果BlackPeople的skinColor 沒有方法體即沒有實現,則會報錯如下:'skinColor' must have a method body because 'BlackPeople' isn't abstract.
- 繼承了抽象類的子類必須實現抽象方法
- 以WhitePeople 為例,如果不實現skinColor 方法會報出如下錯誤:
- Missing concrete implementation of People.skinColor.
- Try implementing the missing method, or make the class abstract.
- 以WhitePeople 為例,如果不實現skinColor 方法會報出如下錯誤:
5. override
"5.1 override 運算符"及override toString
這里筆者對override 運算符添加了引號。至于原因,等大家看完了下邊的內容之后,便會了解筆者的用意。下文提到的override和重寫是一個意思。
先看下運算符重寫的示例代碼:
Vector 類,重寫了+ 運算符和減運算符,以達到Vector可以直接進行加減的目的。筆者還重寫了Vector類的toString 方法,便于查看Vector的x、y值。
class Vector {
final int x;
final int y;
const Vector(this.x, this.y);
Vector operator +(Vector v) {
return Vector(x + v.x, y + v.y);
}
Vector operator -(Vector v) {
return Vector(x - v.x, y - v.y);
}
@override
String toString() {
return 'runtimeType:' + this.runtimeType.toString() + ',x:' + x.toString() +',y:' + y.toString();
}
}
使用Vector的+、-運算符,及重寫toString后,使用Vector的示例代碼及輸出結果如下:
Vector v1 = Vector(1, 1);
Vector v2 = Vector(2, 2);
Vector v3 = v1 + v2;
Vector v0 = v2 - v1;
print(v0);
print(v3);
// 輸出結果:
flutter: runtimeType:Vector,x:1,y:1
flutter: runtimeType:Vector,x:3,y:3
重寫toString的效果是:可控制print的對象的內容及格式。這一點便于非調試環境下查看一些具體錯誤信息。
上文筆者提到了重寫運算符是加引號的原因如下:在筆者看來,運算符的重寫有點跑題了。重寫toString才算是重寫。重寫的toString的返回值、方法名和參數和父類Object都一樣。如大家有不同理解,歡迎討論。
參考學習網址
- http://dart.goodev.org/guides/language/language-tour#classes
- https://dart.dev/guides/language/language-tour#classes
推薦文章:
Dart基礎(一)
Dart基礎(二)
Dart基礎(三)
iOS 短信驗證碼倒計時按鈕
iOS 環境變量配置
iOS 中處理定時任務的常用方法
算法小專欄:貪心算法
iOS 快速實現分頁界面的搭建
iOS 中的界面旋轉