东西还是比较多的,学习一门语言还是比较花时间的,有了C/C++的基础确实会好一点,但是怎么说一个星期的时间还是要画下去的,不然怕是嚼不烂。
主要是根据博客来学习的。
个人感觉是最重要的一个部分,也是必须要会用的一部分。
1.对象
Dart 是一种面向对象的语言,并且支持基于mixin的继承方式。
Dart 语言中所有的对象都是某一个类的实例,所有的类有同一个基类–Object
。
基于mixin的继承方式具体是指:一个类可以继承自多个父类。
但是Dart是单继承,同时可以继承多个“接口”。
使用new
语句来构造一个类,构造函数的名字可能是ClassName
,也可以是ClassName.identifier
, 例如:
var jsonData = JSON.decode('{"x":1, "y":2}');
// Create a Point using Point().
var p1 = new Point(2, 2);
// Create a Point using Point.fromJson().
var p2 = new Point.fromJson(jsonData);
使用.
(dot)来调用实例的变量或者方法。
var p = new 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(new Point(4, 4));
使用?.
来确认前操作数不为空, 常用来替代.
, 避免左边操作数为null引发异常。
使用const
替代new
来创建编译时的常量构造函数。
var p = const ImmutablePoint(2, 2);
使用runtimeType
方法,在运行中获取对象的类型。该方法将返回Type
类型的变量。
print('The type of a is ${a.runtimeType}');
2.实例化变量
在类定义中,所有没有初始化的变量都会被初始化为null
。
class Point {
num x; // Declare instance variable x, initially null.
num y; // Declare y, initially null.
num z = 0; // Declare z, initially 0.
}
类定义中所有的变量, Dart语言都会隐式的定义setter
方法,针对非空的变量会额外增加getter
方法。
class Point {
num x;
num y;
}
main() {
var point = new 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.
}
3.构造函数
Dart 语言中,子类不会继承父类的命名构造函数。如果不显式提供子类的构造函数,系统就提供默认的构造函数。
使用命名构造函数从另一类或现有的数据中快速实现构造函数。
class Point {
num x;
num y;
Point(this.x, this.y);
// 命名构造函数Named constructor
Point.fromJson(Map json) {
x = json['x'];
y = json['y'];
}
}
构造函数不能被继承,父类中的命名构造函数不能被子类继承。如果想要子类也拥有一个父类一样名字的构造函数,必须在子类是实现这个构造函数。
默认情况下,子类只能调用父类的无名,无参数的构造函数; 父类的无名构造函数会在子类的构造函数前调用; 如果initializer list 也同时定义了,则会先执行initializer list 中的内容,然后在执行父类的无名无参数构造函数,最后调用子类自己的无名无参数构造函数。即下面的顺序:
如果父类不显示提供无名无参数构造函数的构造函数,在子类中必须手打调用父类的一个构造函数。这种情况下,调用父类的构造函数的代码放在子类构造函数名后,子类构造函数体前,中间使用:(colon)
分割。
class Person {
String firstName;
Person.fromJson(Map data) {
print('in Person');
}
}
class Employee extends Person {
// 父类没有无参数的非命名构造函数,必须手动调用一个构造函数
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';
}
4.初始化列表
除了调用父类的构造函数,也可以通过初始化列表在子类的构造函数体前(大括号前)来初始化实例的变量值,使用逗号,分隔。如下所示:
class Point {
num x;
num y;
Point(this.x, this.y);
// 初始化列表在构造函数运行前设置实例变量。
Point.fromJson(Map jsonMap)
: x = jsonMap['x'],
y = jsonMap['y'] {
print('In Point.fromJson(): ($x, $y)');
}
}
注意:上述代码,初始化程序无法访问 this 关键字。
5.静态构造函数
如果你的类产生的对象永远不会改变,你可以让这些对象成为编译时常量。为此,需要定义一个const
构造函数并确保所有的实例变量都是final
的。
class ImmutablePoint {
final num x;
final num y;
const ImmutablePoint(this.x, this.y);
static final ImmutablePoint origin = const ImmutablePoint(0, 0);
}
6.重定向构造函数
有时候构造函数的目的只是重定向到该类的另一个构造函数。重定向构造函数没有函数体,使用冒号:
分隔。
class Point {
num x;
num y;
// 主构造函数
Point(this.x, this.y) {
print("Point($x, $y)");
}
// 重定向构造函数,指向主构造函数,函数体为空
Point.alongXAxis(num x) : this(x, 0);
}
void main() {
var p1 = new Point(1, 2);
var p2 = new Point.alongXAxis(4);
}
7.常量构造函数
如果类的对象不会发生变化,可以构造一个编译时的常量构造函数。定义格式如下:
final
。const
声明构造函数。给个例子:
class ImmutablePoint {
final num x;
final num y;
const ImmutablePoint(this.x, this.y);
static final ImmutablePoint origin = const ImmutablePoint(0, 0);
}
8.工厂构造函数
当实现一个使用factory
关键词修饰的构造函数时,这个构造函数不必创建类的新实例。例如,工厂构造函数可能从缓存返回实例,或者它可能返回子类型的实例。 下面的示例演示一个工厂构造函数从缓存返回的对象:
class Logger {
final String name;
bool mute = false;
// _cache 是一个私有库,幸好名字前有个 _ 。
static final Map _cache = {};
factory Logger(String name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
final logger = new Logger._internal(name);
_cache[name] = logger;
return logger;
}
}
Logger._internal(this.name);
void log(String msg) {
if (!mute) {
print(msg);
}
}
}
注意:工厂构造函数不能用 this
。
相信有一点语言基础的都会知道这个东西,所以这里选择从简讨论。
1.实例方法
对象的实例方法可以访问实例变量和this
。以下示例中的distanceTo()
方法是实例方法的一个例子:
import 'dart:math';
class Point {
num x;
num 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);
}
}
2.setters
和Getters
是一种提供对方法属性读和写的特殊方法。每个实例变量都有一个隐式的getter
方法,合适的话可能还会有setter
方法。你可以通过实现getters
和setters
来创建附加属性,也就是直接使用get
和set
关键词:
class Rectangle {
num left;
num top;
num width;
num height;
Rectangle(this.left, this.top, this.width, this.height);
// 定义两个计算属性: 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;
}
main() {
var rect = new Rectangle(3, 4, 20, 15);
assert(rect.left == 3);
rect.right = 12;
assert(rect.left == -8);
}
借助于getter
和setter
,你可以直接使用实例变量,并且在不改变客户代码的情况下把他们包装成方法。
不论是否显式地定义了一个getter
,类似增量++
的操作符,都能以预期的方式工作。为了避免产生任何向着不期望的方向的影响,操作符一旦调用getter
,就会把他的值存在临时变量里。
3.抽象方法
Instance
,getter
和setter
方法可以是抽象的,也就是定义一个接口,但是把实现交给其他的类。要创建一个抽象方法,使用分号;
代替方法体:
abstract class Doer {
// ...定义实例变量和方法...
void doSomething(); // 定义一个抽象方法。
}
class EffectiveDoer extends Doer {
void doSomething() {
// ...提供一个实现,所以这里的方法不是抽象的...
}
}
4.枚举类型
枚举类型,通常被称为enumerations
或enums
,是一种用来代表一个固定数量的常量的特殊类。
声明一个枚举类型需要使用关键字enum
:
enum Color {
red,
green,
blue
}
在枚举中每个值都有一个index getter
方法,它返回一个在枚举声明中从 0 开始的位置。例如,第一个值索引值为 0 ,第二个值索引值为 1 。
assert(Color.red.index == 0);
assert(Color.green.index == 1);
assert(Color.blue.index == 2);
要得到枚举列表的所有值,可使用枚举的values
常量。
List colors = Color.values;
assert(colors[2] == Color.blue);
可以在switch
语句中使用枚举。如果e
在switch (e)
是显式类型的枚举,那么如果你不处理所有的枚举值将会弹出警告:
enum Color {
red,
green,
blue
}
// ...
Color 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'
}
枚举类型有以下限制
5.为类添加特征:mixins
mixins 是一种多类层次结构的类的代码重用。
要使用 mixins ,在with
关键字后面跟一个或多个 mixin 的名字。下面的例子显示了两个使用mixins的类:
class Musician extends Performer with Musical {
// ...
}
class Maestro extends Person with Musical,
Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
要实现 mixin ,就创建一个继承 Object 类的子类,不声明任何构造函数,不调用 super
。例如:
abstract class 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');
}
}
}
6.类的变量与方法
使用static
关键字来实现类变量和类方法。
只有当静态变量被使用时才被初始化。
静态变量, 静态变量(类变量)对于类状态和常数是有用的:
class Color {
static const red = const Color('red'); // 一个恒定的静态变量
final String name; // 一个实例变量。
const Color(this.name); // 一个恒定的构造函数。
}
main() {
assert(Color.red.name == 'red');
}
静态方法, 静态方法(类方法)不在一个实例上进行操作,因而不必访问this
。例如:
import 'dart:math';
class Point {
num x;
num 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);
}
}
main() {
var a = new Point(2, 2);
var b = new Point(4, 4);
var distance = Point.distanceBetween(a, b);
assert(distance < 2.9 && distance > 2.8);
}
考虑到使用高阶层的方法而不是静态方法,是为了常用或者广泛使用的工具和功能。
未完待续