先說(shuō)下題外話哈棚放,最近做了個(gè)領(lǐng)取電商平臺(tái)優(yōu)惠券的小程序紊扬,掃碼支持下哈~
類
dart 是純面向?qū)ο笳Z(yǔ)言,每個(gè)對(duì)象都是某個(gè)類的實(shí)例荣茫,并且所有類都是繼承自 Object。
dart中繼承使用的是 mixin-based 繼承機(jī)制场靴,詳細(xì)理解可以參考Flutter基礎(chǔ):理解Dart的Mixin繼承機(jī)制
訪問(wèn)類的成員
使用點(diǎn)(.)訪問(wèn)實(shí)列變量或者方法
var p = Point(2, 2);
// Set the value of the instance variable y.
p.y = 3;
// Get the value of y.
assert(p.y == 3);
// Invoke distanceTo() on p.
num distance = p.distanceTo(Point(4, 4));
使用(?.)避免當(dāng)左邊對(duì)象為 null 時(shí)拋出異常啡莉。
// If p is non-null, set its y value to 4.
p?.y = 4;
使用構(gòu)造方法
dart中構(gòu)造方法的命名為 ClassName 或者 ClassName.identifier港准。dart2.x 版本中使用構(gòu)造方法創(chuàng)建類實(shí)例時(shí),new 關(guān)鍵字為可選項(xiàng)票罐。
//省略關(guān)鍵字 new
var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});
//不省略關(guān)鍵字 new
var p1 = new Point(2, 2);
var p2 = new Point.fromJson({'x': 1, 'y': 2});
有些類提供常量構(gòu)造方法叉趣,使用常量構(gòu)造方法可以創(chuàng)建編譯時(shí)常量。使用常量構(gòu)造方法需要用 const 替代 new 關(guān)鍵字该押。
var p = const ImmutablePoint(2, 2);
使用一個(gè)類的常量構(gòu)造方法產(chǎn)生的多個(gè)編譯時(shí)常量其實(shí)是同一個(gè)對(duì)象疗杉。
var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);
assert(identical(a, b)); // They are the same instance!
另外,在dart2.x版本下蚕礼,常量上下文中可以省略構(gòu)造方法和字面量之前的 const 關(guān)鍵字烟具。
// Lots of const keywords here.
const pointAndLine = const {
'point': const [const ImmutablePoint(0, 0)],
'line': const [const ImmutablePoint(1, 10), const ImmutablePoint(-2, 11)],
};
// Only one const, which establishes the constant context.
const pointAndLine = {
'point': [ImmutablePoint(0, 0)],
'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)],
};
//構(gòu)造方法不在常量上下文中
var a = const ImmutablePoint(1, 1); // Creates a constant
var b = ImmutablePoint(1, 1); // Does NOT create a constant
assert(!identical(a, b)); // NOT the same instance!
獲取對(duì)象類型
可以使用 Object 的 runtimeType 屬性來(lái)判斷實(shí)例的類型,該屬性返回一個(gè) Type 對(duì)象奠蹬。
print('The type of a is ${a.runtimeType}');
實(shí)例變量
聲明實(shí)例變量朝聋,所有未初始化的實(shí)例變量的值都是 null。
class Point {
num x; // Declare instance variable x, initially null.
num y; // Declare y, initially null.
num z = 0; // Declare z, initially 0.
}
所有實(shí)例變量都有一個(gè)隱式的 getter 方法囤躁。Non-final 實(shí)例變量還會(huì)有一個(gè)隱式的 setter 方法冀痕。
class Point {
num x;
num y;
}
void main() {
var point = Point();
point.x = 4; // Use the setter method for x.
assert(point.x == 4); // Use the getter method for x.
assert(point.y == null); // Values default to null.
}
如果在實(shí)例變量聲明的地方就初始化(不是在構(gòu)造函數(shù)中或者方法中),則該值在實(shí)例創(chuàng)建的時(shí)候就會(huì)被賦值狸演,比構(gòu)造方法和初始化列表執(zhí)行要早言蛇。
構(gòu)造方法
構(gòu)造方法的名字與類名相同,可以使用一些可選標(biāo)識(shí)符進(jìn)行修飾(參考命名構(gòu)造方法)宵距。
class Point {
num x;
num y;
Point(num x, num y) {
// There's a better way to do this, stay tuned.
this.x = x;//this 關(guān)鍵字表示當(dāng)前實(shí)列的引用腊尚,在名稱沖突時(shí)使用
this.y = y;
}
}
將構(gòu)造方法的參數(shù)值賦給實(shí)例變量的使用場(chǎng)景很頻繁,所以dart提供了語(yǔ)法糖來(lái)簡(jiǎn)化满哪。
class Point {
num x, y;
// Syntactic sugar for setting x and y
// before the constructor body runs.
Point(this.x, this.y);
}
默認(rèn)構(gòu)造方法
構(gòu)造方法不會(huì)繼承婿斥,如果沒(méi)有聲明構(gòu)造方法,則dart會(huì)提供一個(gè)默認(rèn)的無(wú)參構(gòu)造方法哨鸭,而且會(huì)調(diào)用超類的無(wú)參構(gòu)造方法民宿。
命名構(gòu)造方法
使用命名構(gòu)造函數(shù)可以為一個(gè)類實(shí)現(xiàn)多個(gè)構(gòu)造函數(shù), 或者使用命名構(gòu)造函數(shù)來(lái)更清晰的表明意圖兔跌。
class Point {
num x;
num y;
Point(this.x, this.y);
// Named constructor
Point.fromJson(Map json) {
x = json['x'];
y = json['y'];
}
}
調(diào)用超類的 non-default 構(gòu)造方法
默認(rèn)情況下勘高,子類調(diào)用超類的非命名、無(wú)參構(gòu)造函數(shù)坟桅, 超類的構(gòu)造函數(shù)在子類構(gòu)造函數(shù)體開(kāi)始執(zhí)行的位置調(diào)用华望。 如果提供了一個(gè) initializer list(初始化參數(shù)列表) ,則初始化參數(shù)列表在超類構(gòu)造函數(shù)執(zhí)行之前執(zhí)行仅乓。下面是構(gòu)造函數(shù)執(zhí)行順序:
- 初始化參數(shù)列表
- 超類無(wú)名構(gòu)造方法
- 主類的無(wú)名構(gòu)造方法
如果超類沒(méi)有無(wú)名無(wú)參數(shù)構(gòu)造函數(shù)赖舟, 則需要手動(dòng)調(diào)用超類的其他構(gòu)造函數(shù)。
class Person {
String firstName;
Person.fromJson(Map data) {
print('in Person');
}
}
class Employee extends Person {
// Person does not have a default constructor;
// you must call super.fromJson(data).
Employee.fromJson(Map data) : super.fromJson(data) {
print('in Employee');
}
}
main() {
var emp = new Employee.fromJson({});
// Prints:
// in Person
// in Employee
if (emp is Person) {
// Type check
emp.firstName = 'Bob';
}
(emp as Person).firstName = 'Bob';
}
由于超類構(gòu)造函數(shù)的參數(shù)在構(gòu)造函數(shù)執(zhí)行之前執(zhí)行夸楣,所以 參數(shù)可以是一個(gè)表達(dá)式或者一個(gè)方法調(diào)用宾抓。如果在構(gòu)造函數(shù)的初始化列表中使用 super()子漩,需要把它放到最后。
class Employee extends Person {
// ...
Employee() : super.fromJson(findDefaultData());
}
初始化列表
在構(gòu)造方法執(zhí)行之前還可以初始化實(shí)例參數(shù)石洗,使用逗號(hào)分隔初始化表達(dá)式幢泼,但是表達(dá)式不能使用this關(guān)鍵字。
class Point {
num x;
num y;
Point(this.x, this.y);
// Initializer list sets instance variables before
// the constructor body runs.
Point.fromJson(Map jsonMap)
: x = jsonMap['x'],
y = jsonMap['y'] {
print('In Point.fromJson(): ($x, $y)');
}
}
初始化列表適合用來(lái)初始化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);
}
重定向構(gòu)造方法
重定向構(gòu)造方法沒(méi)有方法體缕棵,用來(lái)調(diào)用其他構(gòu)造方法。
class Point {
num x;
num y;
// The main constructor for this class.
Point(this.x, this.y);
// Delegates to the main constructor.
Point.alongXAxis(num x) : this(x, 0);
}
常量構(gòu)造方法
用來(lái)提供一個(gè)狀態(tài)不變的實(shí)列對(duì)象涉兽,可以把這些對(duì)象定義為編譯時(shí)常量招驴。
class ImmutablePoint {
static final ImmutablePoint origin =
const ImmutablePoint(0, 0);
final num x, y;
const ImmutablePoint(this.x, this.y);
}
工廠構(gòu)造方法
使用 factory 關(guān)鍵字,提供一個(gè)已經(jīng)緩存的實(shí)例枷畏,或者一個(gè)子類的實(shí)例别厘。
class Logger {
final String name;
bool mute = false;
// _cache is library-private, thanks to
// the _ in front of its name.
static final Map<String, Logger> _cache =
<String, Logger>{};
factory Logger(String name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
final logger = Logger._internal(name);
_cache[name] = logger;
return logger;
}
}
Logger._internal(this.name);
void log(String msg) {
if (!mute) print(msg);
}
}
//使用
var logger = Logger('UI');
logger.log('Button clicked');
方法
實(shí)例方法可以訪問(wèn)實(shí)例變量和this。
import 'dart:math';
class Point {
num x, y;
Point(this.x, this.y);
num distanceTo(Point other) {
var dx = x - other.x;
var dy = y - other.y;
return sqrt(dx * dx + dy * dy);
}
}
getter 和 setter 方法
為對(duì)象屬性提供訪問(wèn)權(quán)限拥诡,每個(gè)實(shí)例變量都有一個(gè)隱式的 getter 方法触趴,如果變量為 non-final ,則還有一個(gè)隱式的 setter 方法渴肉〉癖危可以通過(guò) get、set 關(guān)鍵字宾娜,自定義實(shí)現(xiàn)getter、setter扇售。
class Rectangle {
num left, top, width, 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;
}
void main() {
var rect = Rectangle(3, 4, 20, 15);
assert(rect.left == 3);
rect.right = 12;
assert(rect.left == -8);
}
抽象方法
抽象方法只能存在與抽象類中前塔,沒(méi)有方法體,以逗號(hào)結(jié)尾
abstract class Doer {
// Define instance variables and methods...
void doSomething(); // Define an abstract method.
}
class EffectiveDoer extends Doer {
void doSomething() {
// Provide an implementation, so the method is not abstract here...
}
}
抽象類
使用 abstract 關(guān)鍵字修飾的類就是抽象類承冰,不可以被實(shí)例化华弓。如果定義一個(gè)工廠構(gòu)造方法,讓抽象類看起來(lái)像能夠?qū)嵗?/p>
抽象類一般都有抽象方法困乒。
// This class is declared abstract and thus
// can't be instantiated.
abstract class AbstractContainer {
// Define constructors, fields, methods...
void updateChildren(); // Abstract method.
}
隱式接口
每個(gè)類都隱式的定義了一個(gè)包含所有實(shí)例成員的接口寂屏,并且這個(gè)類實(shí)現(xiàn)了這個(gè)接口。如果想創(chuàng)建類 A 來(lái)支持 類 B 的 api娜搂,而不想繼承 B 的實(shí)現(xiàn)迁霎, 則類 A 應(yīng)該實(shí)現(xiàn) B 的接口。
一個(gè)類可以通過(guò) implements 關(guān)鍵字來(lái)實(shí)現(xiàn)一個(gè)或者多個(gè)接口百宇, 并實(shí)現(xiàn)每個(gè)接口定義的 API考廉。
// A person. The implicit interface contains greet().
class Person {
// In the interface, but visible only in this library.
final _name;
// Not in the interface, since this is a constructor.
Person(this._name);
// In the interface.
String greet(who) => 'Hello, $who. I am $_name.';
}
// An implementation of the Person interface.
class Imposter implements Person {
// We have to define this, but we don't use it.
final _name = "";
String greet(who) => 'Hi $who. Do you know who I am?';
}
greetBob(Person person) => person.greet('bob');
main() {
print(greetBob(new Person('kathy')));
print(greetBob(new Imposter()));
}
擴(kuò)展類
使用 extends 關(guān)鍵字創(chuàng)建子類,使用 super 關(guān)鍵字引用超類携御。
class Television {
void turnOn() {
_illuminateDisplay();
_activateIrSensor();
}
// ···
}
class SmartTelevision extends Television {
void turnOn() {
super.turnOn();
_bootNetworkInterface();
_initializeMemory();
_upgradeApps();
}
// ···
}
覆寫(xiě)
子類可以覆寫(xiě)實(shí)例方法昌粤、getter既绕、setter。使用 @override 注解涮坐,表明是覆寫(xiě)的成員凄贩。
class SmartTelevision extends Television {
@override
void turnOn() {...}
// ···
}
可覆寫(xiě)操作符
< + | []
> / ^ []=
<= ~/ & ~
>= * << ==
– % >>
class Vector {
final int x;
final int y;
const Vector(this.x, this.y);
/// Overrides + (a + b).
Vector operator +(Vector v) {
return new Vector(x + v.x, y + v.y);
}
/// Overrides - (a - b).
Vector operator -(Vector v) {
return new Vector(x - v.x, y - v.y);
}
}
main() {
final v = new Vector(2, 3);
final w = new Vector(2, 2);
// v == (2, 3)
assert(v.x == 2 && v.y == 3);
// v + w == (4, 5)
assert((v + w).x == 4 && (v + w).y == 5);
// v - w == (0, 1)
assert((v - w).x == 0 && (v - w).y == 1);
}
注意,如果覆寫(xiě)了 == 袱讹,則還應(yīng)該覆寫(xiě) hashCode 的getter方法疲扎。
noSuchMethod()
使用 noSuchMethon()可以檢測(cè)并響應(yīng)訪問(wèn)不存在的方法或者實(shí)例變量的情況。
class A {
// Unless you override noSuchMethod, using a
// non-existent member results in a NoSuchMethodError.
@override
void noSuchMethod(Invocation invocation) {
print('You tried to use a non-existent member: ' +
'${invocation.memberName}');
}
}
You can’t invoke an unimplemented method unless one of the following is true:
- The receiver has the static type dynamic.
- The receiver has a static type that defines the unimplemented method (abstract is OK), and the dynamic type of the receiver has an implemention of noSuchMethod() that’s different from the one in class Object.
枚舉
使用 enum 聲明廓译,枚舉的每個(gè)值都有一個(gè) index 的getter方法评肆,index 從0開(kāi)始。
enum Color { red, green, blue }
assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);
使用枚舉的 values 常量可以獲取枚舉的值列表非区。
List<Color> colors = Color.values;
assert(colors[2] == Color.blue);
枚舉可以用在 switch 語(yǔ)句中瓜挽,但是如果不對(duì)枚舉的每個(gè)值都進(jìn)行處理會(huì)警告。
var aColor = Color.blue;
switch (aColor) {
case Color.red:
print('Red as roses!');
break;
case Color.green:
print('Green as grass!');
break;
default: // Without this, you see a WARNING.
print(aColor); // 'Color.blue'
}
枚舉類型具有如下的限制:
- 無(wú)法繼承枚舉類型征绸、無(wú)法使用 mix in久橙、無(wú)法實(shí)現(xiàn)一個(gè)枚舉類型
- 無(wú)法顯示的初始化一個(gè)枚舉類型
為類添加功能:mixin
mixin 是一種多繼承中復(fù)用類代碼的方法,通過(guò) with 關(guān)鍵字來(lái)使用管怠。
class Musician extends Performer with Musical {
// ...
}
class Maestro extends Person
with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
實(shí)現(xiàn) mixin 需要使用 mixin 關(guān)鍵字創(chuàng)建一個(gè)繼承 Object 的未聲明構(gòu)造方法的類淆衷。
mixin Musical {
bool canPlayPiano = false;
bool canCompose = false;
bool canConduct = false;
void entertainMe() {
if (canPlayPiano) {
print('Playing piano');
} else if (canConduct) {
print('Waving hands');
} else {
print('Humming to self');
}
}
}
使用 on 關(guān)鍵字可以定義 mixin 所需要的超類。
mixin MusicalPerformer on Musician {
// ···
}
類變量和方法
使用 static 關(guān)鍵字修飾的類級(jí)別的方法和常量渤弛,dart的靜態(tài)變量命名風(fēng)格為 lowerCamelCase 祝拯。
靜態(tài)變量描述類的狀態(tài)和常量。
class Queue {
static const initialCapacity = 16;
// ···
}
void main() {
assert(Queue.initialCapacity == 16);
}
靜態(tài)變量第一次被使用時(shí)才會(huì)初始化她肯。
靜態(tài)方法
靜態(tài)方法不能對(duì)實(shí)例進(jìn)行操作佳头,也沒(méi)有 this 的訪問(wèn)權(quán)限。
import 'dart:math';
class Point {
num x, y;
Point(this.x, this.y);
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 main() {
var a = Point(2, 2);
var b = Point(4, 4);
var distance = Point.distanceBetween(a, b);
assert(2.8 < distance && distance < 2.9);
print(distance);
}
注意: 對(duì)于通用的或者經(jīng)常使用的靜態(tài)函數(shù)晴氨,考慮使用頂級(jí)方法而不是靜態(tài)函數(shù)康嘉。
靜態(tài)函數(shù)還可以當(dāng)做編譯時(shí)常量使用。例如,可以把靜態(tài)函數(shù)當(dāng)做常量構(gòu)造函數(shù)的參數(shù)來(lái)使用籽前。