Dart是一個面向對象的語言,同時增加了混入(mixin)繼承的特性。對象都是由類初始化生成的,所有的類都由Object對象繼承。混入繼承意味著儘管所有類(除了Object類)只有一個父類,但是類的代碼體可以在多個類中重覆使用。(個人理解:mixin,extends,implements,exte ...
Dart是一個面向對象的語言,同時增加了混入(mixin)繼承的特性。對象都是由類初始化生成的,所有的類都由Object對象繼承。混入繼承意味著儘管所有類(除了Object類)只有一個父類,但是類的代碼體可以在多個類中重覆使用。(個人理解:mixin,extends,implements,extends是類似java單繼承,implements類似java的多介面實現,但這些都是運行時使用,mixin應該是在編譯時使用,類似於jsp的include,比如 A with B,其實是在編譯時,將兩個類的代碼合併,編譯成classes) 。
Using class members 類成員
對象由成員變數和方法構成。你可以用點(.)調用方法或者成員變數
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));
Use ?. instead of . to avoid an exception when the leftmost operand is null:
// If p is non-null, set its y value to 4.
p?.y = 4;
Using constructors 構造方法
你可以用構造方法創建對象,構造方法是ClassName或者ClassName.identifier.下麵是構造方法的兩種寫法:
Point() and Point.fromJson() constructors:
var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});
下麵的構造方法多了new關鍵字:
var p1 = new Point(2, 2);
var p2 = new Point.fromJson({'x': 1, 'y': 2});
版本註意:new 關鍵字在Dart2中是可選的
。
一些類提供了靜態構造方法。當你需要這些創建編譯常量對象,可以在構造對象代碼前加上const關鍵字。
var p = const ImmutablePoint(2, 2);
兩個相同入參的編譯對象常量是相等。
var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);
assert(identical(a, b)); // They are the same instance!
在編譯常量上下中,可以忽略後續的const關鍵字,也可以達到同樣效果
// 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)],
};
如果用常量構造器初始化對象,但是沒有加入const關鍵字,它將是非常量對象
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!
版本註意: const關鍵字的忽略寫法在Dart2中才有效
Getting an object’s type 獲取對象類型
在運行時獲取對象的類型,你可以用Object的對象的 runtimeType屬性。
print('The type of a is ${a.runtimeType}');
Instance variables 成員變數
下麵是如何定義成員變數:
class Point {
num x; // Declare instance variable x, initially null.
num y; // Declare y, initially null.
num z = 0; // Declare z, initially 0.
}
所有為初始化變數的值是null.
所有成員變數都內置get方法。非final的成員變數內置了set方法。
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.
}
如果你在成員變數的定義時就進行初始化,在對象創建時,它將先於構造器和初始化列表執行。
Constructors 構造器
類構造器是和類名同名的函數,(其中還有一種命名構造函數的寫法,後續介紹)。
class Point {
num x, y;
Point(num x, num y) {
// There's a better way to do this, stay tuned.
this.x = x;
this.y = y;
}
}
上面的例子中,thisg關鍵字可以讀取成員變數。
註意:使用this只有在方法參數和成員變數有衝突時才用,其它場景可以不寫
如果構造器的入參和成員變數對應,可以直接賦值,可以用如下的簡化寫法:
class Point {
num x, y;
// Syntactic sugar for setting x and y
// before the constructor body runs.
Point(this.x, this.y);
}
Default constructors 預設構造器
如果沒有定義構造器,類將提供一個無參構造器,如果類有一個父類,也會調用父類的無參構造器
Constructors aren’t inherited 構造器不可繼承
子類不會繼承父類的構造器,如果子類沒有定義構造器,它只有預設的無參構造器,不會去繼承父類的構造器。
Named constructors 命名構造器
Dart可以使用命名構造器去實現多個構造邏輯。
class Point {
num x, y;
Point(this.x, this.y);
// Named constructor
Point.origin() {
x = 0;
y = 0;
}
}
構造器是不可繼承的,這個規則在命名構造器同樣有效。如果你想要一個和父類一樣的命名構造器,需要在子類的自己定義同樣的構造器。
Invoking a non-default superclass constructor 調用父類的非預設構造器
預設情況下,子類的構造器將調用父類的預設構造器。父類構造器先於子類構造器執行,如果子類構造器有初始化執行器列表,它將先於父類構造器執行,如下是執行順序:
initializer list
superclass’s no-arg constructor
main class’s no-arg constructor
如果父類沒有預設構造器,你必須手動調用父類的其它構造器。寫法是在子類構造器名後面加上冒號(:),寫上父類構造器調用。
Because the arguments to the superclass constructor are evaluated before invoking the constructor, an argument can be an expression such as a function call:
class Employee extends Person {
Employee() : super.fromJson(getDefaultData());
// ···
}
註意:父類構造器的參數不接收this, 只能接受靜態方法。
Initializer list 初始化執行器
在構造器後面除了可以追加父類構造器外,還可以追加初始化執行器。
// Initializer list sets instance variables before
// the constructor body runs.
Point.fromJson(Map<String, num> json)
: x = json['x'],
y = json['y'] {
print('In Point.fromJson(): ($x, $y)');
}
註意:右邊的初始化執行器不接受this寫法。
在開發模式下,你可以通過assert在初始化執行器中判斷參數是否為空。
Point.withAssert(this.x, this.y) : assert(x >= 0) {
print('In Point.withAssert(): ($x, $y)');
}
Redirecting constructors 重定向構造器
有些時候,定義的構造器只為了傳遞給另一個構造器,那麼構造器的代碼體是空的,同時後面直接追加:目標構在器
class Point {
num x, y;
// The main constructor for this class.
Point(this.x, this.y);
// Delegates to the main constructor.
Point.alongXAxis(num x) : this(x, 0);
}
Constant constructors 常量構造器
如果對象在創建後,不再改變,你可以將它寫成編譯常量。這些樣類,首先構造器要有const關鍵字,同時成員變數得是final.
class ImmutablePoint {
static final ImmutablePoint origin =
const ImmutablePoint(0, 0);
final num x, y;
const ImmutablePoint(this.x, this.y);
}
Factory constructors 工廠構造器
當構造器使用factory關鍵字時,對象創建不一定每次都是一個新對象。factory可能從緩存中提取對象返回或者創建新對象。
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);
}
}
註意:工廠對象不能使用this關鍵字
。
調用工廠構造器和其它構造器的寫法沒差別。
var logger = Logger('UI');
logger.log('Button clicked');
Methods 方法
Instance methods 成員方法
成員方法可以使用成員變數和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);
}
}
Getters and setters get和set
get和set是讀寫對象成語變數的特殊方法。通過.調用成員變數,就是調用內置的get方法,set方法也是一樣的。
你也可以通過get或者set關鍵字重載get和set方法
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);
}
Note: Operators such as increment (++) work in the expected way, whether or not a getter is explicitly defined. To avoid any unexpected side effects, the operator calls the getter exactly once, saving its value in a temporary variable.
Abstract methods 抽象方法
成員方法,get方法,set方法都是定義為在抽象類中定義成抽象方法,註意是在抽象類中,非抽象類不可定義。
抽象方法直接在方法後加上;號,不用實現代碼。
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 classes 抽象類
你可以用abstract關鍵字來定義抽象類,註意,抽象類不可初始化。抽象類適合做介面定義,具體實現通過繼承抽象類。抽象類的實現初始化方法,一般用工廠構造器。
// This class is declared abstract and thus
// can't be instantiated.
abstract class AbstractContainer {
// Define constructors, fields, methods...
void updateChildren(); // Abstract method.
}
Implicit interfaces 介面實現
如果A繼承B,則A將具備B的所有成員變數和方法,也同時具備B繼承的所有介面方法。但是,你如果不像繼承B類,但有希望擁有B類的方法,可以繼承B的相應介面。
// 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(String who) => 'Hello, $who. I am $_name.';
}
// An implementation of the Person interface.
class Impostor implements Person {
get _name => '';
String greet(String who) => 'Hi $who. Do you know who I am?';
}
String greetBob(Person person) => person.greet('Bob');
void main() {
print(greetBob(Person('Kathy')));
print(greetBob(Impostor()));
}
下麵是多介面繼承例子,
class Point implements Comparable, Location {...}
Extending a class 擴展類
通過extends繼承父類,可以用super去訪問父類。
class Television {
void turnOn() {
_illuminateDisplay();
_activateIrSensor();
}
// ···
}
class SmartTelevision extends Television {
void turnOn() {
super.turnOn();
_bootNetworkInterface();
_initializeMemory();
_upgradeApps();
}
// ···
}
Overriding members 重載
子類可以重載成員方法,get方法,set 方法,你可以用@override是標記該方法被重載
class SmartTelevision extends Television {
@override
void turnOn() {...}
// ···
}
To narrow the type of a method parameter or instance variable in code that is type safe, you can use the covariant keyword.
Overridable operators 操作符重載
你可以重載如下操作符:
< + | []
/ ^ []=
<= ~/ & ~
= * << ==
– % >>
‘註意:你會註意到!=是不用於重載的,因為它和==是等效的,比如(e1!=e2)和!(e1==e2)這兩個是相同效果'
下麵是+,-的重載示例:
class Vector {
final int x, y;
Vector(this.x, this.y);
Vector operator +(Vector v) => Vector(x + v.x, y + v.y);
Vector operator -(Vector v) => Vector(x - v.x, y - v.y);
// Operator == and hashCode not shown. For details, see note below.
// ···
}
void main() {
final v = Vector(2, 3);
final w = Vector(2, 2);
assert(v + w == Vector(4, 5));
assert(v - w == Vector(0, 1));
}
註意:如果你重載了==,也需要重載hashCode方法。
noSuchMethod()
如果對象嘗試調用不存在的變數或者方法,會拋出noSuchMethod異常,這個異常可以通過重載該方法,增加自定義邏輯。
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}');
}
}
你一般不能調用未繼承的方法除非出現如下情況:
它是一個dynamic對象,因為是動態類型,所以編譯器不會檢查。
它繼承了一個抽象類,同時它沒有做抽象方法做具體實現,但是定義了 noSuchMethod()函數,這樣,編譯器也不會檢查。
更多的內容請查看noSuchMethod
Enumerated types 枚舉類型
枚舉類型一般用於表示一些限定範圍內的常量。
Using enums
可以用enum關鍵字定義枚舉。
enum Color { red, green, blue }
每個枚舉對象的屬性都會賦予一個整數值,它是從0開始,基於屬性位置遞增。例如,Color的枚舉對象,第一個是0,第二個是1
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語句,編譯器會在你沒有處理所有枚舉屬性時,作出提示。
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'
}
枚舉類型有下麵的限制:
枚舉不可繼承,混入,也不可以被繼承
枚舉類不可初始化
Adding features to a class: mixins 混入
Mixins是一種在多個類中重用代碼的方式。
混入的寫法是:with關鍵字加上多個混入的類
class Musician extends Performer with Musical {
// ···
}
class Maestro extends Person
with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
用於mixin的對象是一個繼承Object的無構造函數的類,如果你想將用於混入的類和普通類區分開來,可以用 mixin關鍵字替代class.
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');
}
}
}
mixin可以通過on關鍵字來限定哪些類的子類才可以用它做用戶(不太理解:To specify that only certain types can use the mixin — for example, so your mixin can invoke a method that it doesn’t define — use on to specify the required superclass:)
mixin MusicalPerformer on Musician {
// ···
}
註意:mixin關鍵字在dart2.1採用到,早期版本用abstract class
.
Class variables and methods 類成員變數和方法
使用static 關鍵字修飾類的成員變數和方法就形成了靜態變數和靜態方法。
Static variables 靜態變數
Static variables (class variables) are useful for class-wide state and constants:
class Queue {
static const initialCapacity = 16;
// ···
}
void main() {
assert(Queue.initialCapacity == 16);
}
靜態變數在它使用時才初始化。
Static methods 靜態方法
靜態方法可以通過類直接調用:
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);
}
第八篇翻譯 ,Generics 泛型