学习笔记04
东西还是比较多的,学习一门语言还是比较花时间的,有了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 中的内容,然后在执行父类的无名无参数构造函数,最后调用子类自己的无名无参数构造函数。即下面的顺序:
- initializer list(初始化列表)
- super class’s no-arg constructor(父类无参数构造函数)
- main class’s no-arg constructor (主类无参数构造函数)
-
如果父类不显示提供无名无参数构造函数的构造函数,在子类中必须手打调用父类的一个构造函数。这种情况下,调用父类的构造函数的代码放在子类构造函数名后,子类构造函数体前,中间使用
:(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<String, Logger> _cache = <String, Logger>{};
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<Color> 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); }
考虑到使用高阶层的方法而不是静态方法,是为了常用或者广泛使用的工具和功能。
- 你可以将静态方法作为编译时常量。例如,你可以把静态方法作为一个参数传递给静态构造函数。
未完待续