原文:https://www.dartlang.org/guides/language/language-tour
Dart是一種面向?qū)ο蟮恼Z(yǔ)言,具有類和基于mixin的繼承。 每個(gè)對(duì)象都是一個(gè)類的實(shí)例,所有類都來自O(shè)bject众旗。 基于Mixin的繼承意味著雖然每個(gè)類(除了Object)只有一個(gè)超類呕缭,但是類體可以在多個(gè)類層次結(jié)構(gòu)中重用。
用點(diǎ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));
用 ?.
i代替 .
來避免異常當(dāng)左對(duì)象是null:
// If p is non-null, set its y value to 4.
p?.y = 4;
使用構(gòu)造函數(shù)
你可以用構(gòu)造函數(shù)來創(chuàng)建一個(gè)對(duì)象. 構(gòu)造函數(shù)可以用 類名或者類名.標(biāo)識(shí)符. 例如, 下面的代碼使用使用 Point()
和 Point.fromJson()
構(gòu)造函數(shù)來創(chuàng)建 Point
對(duì)象:
var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});
下面的代碼具有同樣的效果, but uses the optional new
keyword before the constructor name:
var p1 = new Point(2, 2);
var p2 = new Point.fromJson({'x': 1, 'y': 2});
Version note: new
關(guān)鍵字在Dart 2中是可選的.
一些類提供 constant constructors. 用常量構(gòu)造函數(shù)來創(chuàng)建編譯時(shí)常量, 將 const
關(guān)鍵字放在構(gòu)造函數(shù)前面:
var p = const ImmutablePoint(2, 2);
Constructing two identical compile-time constants results in a single, canonical instance:
var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);
assert(identical(a, b)); // They are the same instance!
在 常量上下文(constant context)中, 你可以省略 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)],
};
省略const
:
// Only one const, which establishes the constant context.
const pointAndLine = {
'point': [ImmutablePoint(0, 0)],
'line': [ImmutablePoint(1, 10), ImmutablePoint(-2, 11)],
};
若常量構(gòu)造函數(shù)沒有在常量上下文中被調(diào)用椒楣,或者沒有用const
調(diào)用, 會(huì)產(chǎn)生 non-constant object錯(cuò)誤:
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!
Version note: const
keyword became optional within a constant context in Dart 2.
獲得對(duì)象類型
在運(yùn)行時(shí)獲取對(duì)象類型, 你可以使用對(duì)象的 runtimeType
屬性, 返回 Type object.
print('The type of a is ${a.runtimeType}');
實(shí)例變量
聲明:
class Point {
num x; // Declare instance variable x, initially null.
num y; // Declare y, initially null.
num z = 0; // Declare z, initially 0.
}
所有未初始化變量的值都是 null
.
所有實(shí)例變量都含有有隱式的getter 方法. 非final
實(shí)例變量都有隱式的 setter 方法. 細(xì)節(jié)請(qǐng)看 Getters and setters.
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.
}
當(dāng)你在聲明實(shí)例變量的地方初始化它 (不是在構(gòu)造函數(shù)和方法內(nèi)),當(dāng)實(shí)例變量創(chuàng)建的時(shí)候就會(huì)被賦值, 在構(gòu)造方法和初始化列表執(zhí)行之前.
Dart語(yǔ)法糖:
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)造函數(shù)
如果你沒有聲明構(gòu)造函數(shù), 會(huì)為你提供一個(gè)默認(rèn)構(gòu)造函數(shù). 默認(rèn)構(gòu)造函數(shù)沒有任何參數(shù)并且會(huì)調(diào)用父類的無參構(gòu)造函數(shù)。構(gòu)造函數(shù)不能被繼承牡肉。
子類不能繼承父類的構(gòu)造函數(shù). 沒有聲明構(gòu)造函數(shù)的子類只有一個(gè)默認(rèn)構(gòu)造函數(shù).
命名構(gòu)造函數(shù)
使用命名構(gòu)造函數(shù)來為類提供多個(gè)構(gòu)造函數(shù):
class Point {
num x, y;
Point(this.x, this.y);
// Named constructor
Point.origin() {
x = 0;
y = 0;
}
}
父類的命名構(gòu)造函數(shù)不能被子類繼承.如果你想用父類的命名構(gòu)造函數(shù)來創(chuàng)建子類, 你必須在子類中實(shí)現(xiàn)它.
調(diào)用父類的非默認(rèn)構(gòu)造函數(shù)
默認(rèn)的, 子類的構(gòu)造函數(shù)調(diào)用父類的非命名無參構(gòu)造函數(shù). 父類的構(gòu)造函數(shù)在子類構(gòu)造函數(shù)體的開始執(zhí)行之前被調(diào)用. 如果使用了初始化列表 捧灰,初始化列表會(huì)在父類構(gòu)造函數(shù)之前執(zhí)行. 總之,執(zhí)行順序如下:
- 初始化列表 initializer list
- 父類的構(gòu)造函數(shù)
- 主類的構(gòu)造函數(shù)
如果父類中沒有非命名無參構(gòu)造函數(shù), 你必須手動(dòng)調(diào)用父類的一個(gè)構(gòu)造函數(shù). 在冒號(hà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';
}
因?yàn)楦割悩?gòu)造函數(shù)的參數(shù)會(huì)在調(diào)用父類構(gòu)造函數(shù)之前被計(jì)算出來统锤,參數(shù)可以是一個(gè)表達(dá)式毛俏,例如一個(gè)函數(shù)調(diào)用:
class Employee extends Person {
Employee() : super.fromJson(getDefaultData());
// ···
}
警告: 父類構(gòu)造函數(shù)的參數(shù)沒有
this
權(quán)限。例如參數(shù)可以調(diào)用靜態(tài)方法饲窿,但不能調(diào)用實(shí)例方法.
初始化列表
除了調(diào)用父類構(gòu)造函數(shù)煌寇,你還可以在構(gòu)造函數(shù)執(zhí)行之前初始化實(shí)例變量. 用逗號(hào)分隔初始化代碼.
// 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)');
}
警告: The right-hand side of an initializer does not have access to this.
在開發(fā)中,你可以在初始化列表中用assert
來驗(yàn)證輸入.
Point.withAssert(this.x, this.y) : assert(x >= 0) {
print('In Point.withAssert(): ($x, $y)');
}
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)造函數(shù)
有時(shí)構(gòu)造函數(shù)的唯一目的就是定位到另一個(gè)同類中的其他構(gòu)造函數(shù). 重定位構(gòu)造函數(shù)的函數(shù)體是空的逾雄,with the constructor call appearing after a colon (:).
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);
}
常量構(gòu)造函數(shù)
如果你的類產(chǎn)生的對(duì)象從不改變,你可以把這些對(duì)象聲明為編譯時(shí)常量. 為此阀溶,定義一個(gè)const
構(gòu)造函數(shù)來確保所有實(shí)例變量都是 final
.
class ImmutablePoint {
static final ImmutablePoint origin =
const ImmutablePoint(0, 0);
final num x, y;
const ImmutablePoint(this.x, this.y);
}
常量構(gòu)造函數(shù)并不總是創(chuàng)建常量. 細(xì)節(jié)請(qǐng)看using constructors.
工廠構(gòu)造函數(shù)
當(dāng)實(shí)現(xiàn)一個(gè)并不總是創(chuàng)建新實(shí)例對(duì)象的構(gòu)造函數(shù),使用factory
關(guān)鍵詞. 例如鸦泳,一個(gè)工廠構(gòu)造函數(shù)可能從緩存中返回一個(gè)對(duì)象银锻,或者它可能返回一個(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);
}
}
Note: 工廠構(gòu)造函數(shù)不能訪問this.
像調(diào)用其他構(gòu)造函數(shù)一樣調(diào)用工廠構(gòu)造函數(shù):
var logger = Logger('UI');
logger.log('Button clicked');
方法
實(shí)例方法
對(duì)象的實(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);
}
}
Getters and setters
Getters and setters 是為類屬性提供讀寫途徑的特殊方法。 重新強(qiáng)調(diào)做鹰,每個(gè)實(shí)例變量都有一個(gè)隱式的getter和 setter . 你可以通過實(shí)現(xiàn)getters 和 setters來創(chuàng)建額外的屬性, 使用 get
和 set
關(guān)鍵詞:
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);
}
With getters and setters, you can start with instance variables, later wrapping them with methods, all without changing client code.
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.
抽象方法
getter, setter 可以是抽象的, 定義一個(gè)接口并把它的實(shí)現(xiàn)交給其他類. 抽象方法只能存在于 抽象類.
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...
}
}
隱式接口
每個(gè)類都隱式的定義了一個(gè)包含一個(gè)類和它實(shí)現(xiàn)的所有接口的所有實(shí)例變量和方法. 如果你想創(chuàng)建一個(gè)支持B類API但不繼承其實(shí)現(xiàn)的類A击纬,A類應(yīng)該實(shí)現(xiàn)B類接口.
一個(gè)類可以實(shí)現(xiàn)一個(gè)或多個(gè)接口:
// 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 {...}
擴(kuò)展類
class Television {
void turnOn() {
_illuminateDisplay();
_activateIrSensor();
}
// ···
}
class SmartTelevision extends Television {
void turnOn() {
super.turnOn();
_bootNetworkInterface();
_initializeMemory();
_upgradeApps();
}
// ···
}
重寫方法
class SmartTelevision extends Television {
@override
void turnOn() {...}
// ···
}
可重載運(yùn)算符
< + | []
> / ^ []=
<= ~/ & ~
>= * << ==
– % >>
Note: 你可能會(huì)被通知!=
不是個(gè)可重載的操作符.表達(dá)式1e1 != e2 `i只是!(e1 == e2)的語(yǔ)法糖.
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));
}
如果你重載了==
, 你應(yīng)該重寫Object
的hashCode
getter. 例子請(qǐng)看 Implementing map keys.
noSuchMethod()
為了探測(cè)和對(duì)是否調(diào)用了類中不存在的方法和變量作出反應(yīng),你可以重寫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}');
}
}
你 不能調(diào)用 未實(shí)現(xiàn)的方法除非以下之一成立:
- 接收者有靜態(tài)類型
dynamic
. - 接收者是靜態(tài)類型定義了未實(shí)現(xiàn)的方法 (抽象的也行), 接收者的動(dòng)態(tài)類型有異于
Object
的noSuchMethod()
的實(shí)現(xiàn).
更多請(qǐng)看noSuchMethod forwarding specification.
Enum類型
通常叫列舉或枚舉, 是一種特殊的類用來代表常量值的固定數(shù)字.
使用enum
enum Color { red, green, blue }
enum
中的每個(gè)值都有個(gè) index
getter, 返回從0開始的值在enum中聲明的位置.例如, 第一個(gè)值的index為0, 第二個(gè)為1.
assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);
To get a list of all of the values in the enum, use the enum’s values constant.
List<Color> colors = Color.values;
assert(colors[2] == Color.blue);
你可以在 switch statements中使用enum, 你如果沒有處理全部值你會(huì)得到一個(gè)警告:
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'
}
但enum
類型有如下限制:
- You can’t subclass, mix in, or implement an enum.
- You can’t explicitly instantiate an enum.
更多請(qǐng)看Dart language specification.
為類添加特性: 混入
混入是一種在多層級(jí)類關(guān)系重用類代碼的方法。
使用混入钾麸,將一個(gè)或多個(gè)mixin名放在with
后面更振。
class Musician extends Performer with Musical {
// ···
}
class Maestro extends Person
with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
實(shí)現(xiàn)一個(gè)mixin炕桨,創(chuàng)建一個(gè)繼承自O(shè)bject的類,并且不聲明任何構(gòu)造函數(shù)殃饿。除非你想你的mixin可以像正常類一樣被使用谋作,否則用mixin
關(guān)鍵詞來代替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');
}
}
}
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 {
// ···
}
Version note: mixin
關(guān)鍵詞在 Dart 2.1中引進(jìn). 早期版本的代碼使用abstract class
代替. 更多請(qǐng)看Dart SDK changelog and 2.1 mixin specification.