内容简介:Dart是面向对象的语言,所有对象都是一个类的实例,所有类都继承自类的实例化,可以使用如果要访问对象的成员(
Dart是面向对象的语言,所有对象都是一个类的实例,所有类都继承自 Object
。此外, Dart
支持基于 mixin
的继承机制,这意味着,每个类(除了 Object
)都只有一个父类,一个类的代码可以在其他多个类继承中重复使用。
一、类的实例化
类的实例化,可以使用 new
关键字,后面可以接上 构造函数
(如 ClassName
或者 ClassName.identifier
),如:
var jsonData = JSON.decode('{"x":1, "y":2}'); // 直接接 ClassName var p1 = new Point(2, 2); // 接 ClassName.identifier var p2 = new Point.fromJson(jsonData);
如果要访问对象的成员( 属性
或者 方法
),可以使用 .
语法:
var distance = p1.distanceTo(p2);
还可以使用 ?.
来避免左边对象为 null
时抛出异常:
p?.y = 4; // 如果p为非null,才会执行 p.y = 4;
有些类提供了 常量构造函数
,可以创造 编译时常量
,那么这种构造函数的实例化不采用 new
关键字,而是采用 const
关键字,如下:
var p = const ImmutablePoint(2, 2);
两个一样的编译时常量是同一个对象,用 identical
测试返回 true
:
var a = const ImmutablePoint(1, 1); var b = const ImmutablePoint(1, 1); identical(a, b); // true
此外, runtimeType
可以获得一个示例的类型,该属性返回一个 Type
对象:
main() { int a = 1; print('type of a is ${a.runtimeType}'); } // 输出:type of a is int
二、类的定义
使用 class
关键字可以声明一个类:
class Foo { }
1)实例变量
属于类的实例的变量称为 实例变量
, 实例变量
使用 var propertyName;
或者 type propertyName
声明在 class
体里,如果一个 实例变量
没有赋予初始值,则它的初始值是 null
,如:
class Point { num x; // 初始值 null num y; // 初始值 null num z; }
每个实例变量都会隐含地生成一个 getter
方法,对于非 final
的变量还会隐含地生成一个 setter
,所以可以:
var point = new Point(); point.x = 4; // 调用了 setter 方法来设置变量值 point.y; // 调用了 getter 方法来获取变量值
注意:如果在定义实例变量时初始化了 实例变量
,那么实例变量的值是在实例创建时、构造函数和初始化参数列表 执行前
初始化的
2)构造函数
class
中命名和 类名
一致的那个函数是 构造函数
,构造函数会在 类
被实例化时调用,如:
class Point { num x; num y; Point(num x, num y) { this.x = x; this.y = y; } }
其中 this
关键字指向当前的实例。不过,只有当名字冲突时才使用 this
, Dart
中在类里是可以忽略 this
的,也就是说,我们可以这么写:
class Point { num x; num y; Point(num _x, num _y) { x = _x; y = _y; } }
或者,也可以直接在构造函数中使用 初始化赋值
的语法糖:
class Point { num x; num y; Point(this.x, this.y); }
1、默认构造函数
如果在一个类中没有定义构造函数,则会有个默认的构造函数。默认的构造函数无参数,且会调用没有参数的构造函数。
2、构造函数不会继承
子类
不会继承 父类
的构造函数(除非是无名无参的构造函数)
3、命名构造函数
使用命名构造函数可以实现为一个类指定 多个构造函数
,也可以通过这来更清晰地表达意图,如:
class Point { num x; num y; Point(this.x, this.y); Point.fromJson(Map json) { x = json['x']; y = json['y']; } }
4、调用父类的构造函数
默认情况下,子类的构造函数会自动调用父类的 无名无参的默认构造函数
,父类的构造函数在子类构造函数的函数体开头位置调用。但是如果提供了 初始化参数列表
,则初始化参数列表会在 父类构造函数
之前执行,也就是说,执行顺序如下:
- 初始化参数列表
- 父类无名构造函数
- 子类无名构造函数
如果超类中没有 无名无参
构造函数,那么就需要手动调用了,调用方法为在 构造函数
之后使用 :
调用,如:
class Point { num x; num y; Point(this.x, this.y); } class Point3D extends Point { num z; Point3D(num x, num y, num z): super(x, y) { this.z = z; } }
由于 父类构造函数
是在 子类构造函数
执行前执行的,所以参数可以是一个表达式或者一个方法调用,如:
class A { A(String str) { print(str); } } class B extends A { B(): super(getDefaultData()); static getDefaultData() { return 'Hello, world'; } }
注意:如果是一个类中的 方法
调用,那么只能使用静态方法
5、初始化列表
初始化列表除了可以调用父类构造函数,还可以初始化实例参数,采用 ,
分隔表达式,如:
class Point { num x; num y; Point(this.x, this.y); Point.fromJson(Map jsonMap): x = jsonMap['x'], y = jsonMap['y'] { print('($x, $y)'); } }
需要注意的是:初始化表达式 =
右边的部分不能访问 this
,此外,对于 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); }
6、重定向构造函数
有时候一个 构造函数
会调用类中的其他构造函数,这种构造函数称之为 重定向构造函数
,如下:
class Point { num x; num y; Point(this.x, this.y); Point.alongXAxis(num x): this(x, 0); }
7、常量构造函数
如果类提供的是状态不变的对象,那么可以把这些对象定义为 编译时常量
,实现这种功能可以定义 const
构造函数,且声明所有类的变量为 final
,如:
class ImmutablePoint { final num x; final num y; const ImmutablePoint(this.x, this.y); static final ImmutablePoint origin = const Immutable(0, 0); }
8、工厂方法构造函数
如果一个构造函数并不总是返回一个新的对象,那么可以在构造函数前面加上 factory
关键字,来表示它是一个 工厂方法构造函数
。但是需要注意的是,工厂构造函数中不能访问 this
,如下:
class Logger { final String name; bool mute = false; // 缓存实例 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); } } }
那么,在使用 new
关键词实例化 Logger
时,每次都会调用 factory Logger(String name)
这个构造函数:
var logger1 = new Logger('UI'); var logger2 = new Logger('UI'); identical(logger1, logger2); // true
3)实例方法
实例方法可以访问 this
, Dart
中实例方法的声明如下:
class A { methodName() { // ... } }
4)setter/getter
在一个方法前面加上 setter
或 getter
关键字,那么这个方法就成为了 setter
和 getter
,就可以对 instance.methodName
进行赋值或者取值操作,而无需使用 ()
来调用,如:
class Person { String firstName; String lastName; Person(this.firstName, this.lastName); String get fullName { return '$firstName $lastName'; } } final me = new Person('Ruphi', 'Lau'); print(me.fullName); // 输出:Ruphi Lau
注意, getter
里不能带 ()
,即 get name()
是错误的, get name
才是正确的,而 setter
里则可以接受一个参数,即为赋值传入的值: set name(String name)
三、操作符重写
Dart
中支持操作符重写,可被重写的操作符有:
-
比较运算符:
>
,<
,<=
,>=
,==
-
算数运算符:
+
,-
,*
,/
,%
,~/
-
位运算符:
|
,&
,^
,~
,<<
,>>
-
方括号运算符:
[]
,[]=
重写运算符的语法为使用 operator
关键字紧接运算符,以下例子为实现 向量
的运算:
class Vector { final int x; final int y; const Vector(this.x, this.y); Vector operator +(Vector v) { return new Vector(x + v.x, y + v.y); } 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); final addRes = v + w; print('(${addRes.x}, ${addRes.y})'); // 输出:(4, 5) final minusRes = v - w; print('(${minusRes.x}, ${minusRes.y})'); // 输出:(0, 1) }
四、抽象类
1)抽象类的定义
不能被实例化的类是抽象类,抽象类通常用来定义接口及 部分实现
。如果抽象类要被实例化,则需要定义一个 工厂构造函数
。抽象类的声明使用 abstract
修饰符:
abstract class AbstractContainer { // ... }
2)抽象函数
抽象函数是之定义函数接口但是没有实现(方法体)的函数,抽象函数由子类实现,调用一个未实现的抽象函数会导致运行时异常。如下:
abstract class Doer { void doSomething(); // 抽象函数,没有方法体 } class EffectiveDoer extends Doer { void doSomething() { // 在子类中实现 } }
3)隐式接口
每个类都隐式地定义了一个包含所有实例成员的接口,并且这个类实现了该接口。 Dart
中并没有直接提供 interface
这样子的关键字,因此定义 interface
应该通过定义一个类实现,如果只想支持某个类的接口但是不想继承它的实现,那么使用 implements
关键字即可,如下:
class Person { final name; Person(this.name); // 构造函数不会创建接口 String greet(who) => 'Hello, $who. I am $name'; // 包含了 greet 的实现 } class Stark implements Person { final name = 'Tony Stark'; String greet(who) => '$who, I am, I am $name'; } main() { final ironMan = new Stark(); print( ironMan.greet('Thanos') ); }
接口是可以 多实现
的,如下:
class TonyStart implements American, Scientist, Richman, Playboy { // ... }
五、类的继承
类的继承,采用 extends
关键字,而 子类
中可以使用 supper
来引用 父类
,如下:
class TV { void turnOn() { _illuminateDisplay(); _activateIrSensor(); } // ... } class SmartTV extends TV { void turnOn() { super.turnOn(); _bootNetworkInterface(); _initializeMemory(); _upgradeApps(); } // ... }
子类可以覆写 实例函数
, getter
和 setter
。以下例子为覆写 noSuchMethod()
函数(这个函数为 Object
类中当调用了对象上不存在的函数所触发的):
class A { void noSuchMethod(Invocation mirror) { print('You tried to use a non-existent member: ${mirror.memberName}'); } }
此外,可以使用 @override
注解来表明是想要覆写超类的一个函数,如:
class A { @override void noSuchMethod(Invocation mirror) { // ... } }
六、枚举类型
枚举类型是一种特殊的 类
,用来表示枚举,使用 enum
关键字可以定义枚举:
enum Color { RED, GREEN, BLUE }
枚举类型中的每个值都有一个 index getter
,返回枚举值在定义中出现的位置(从0开始):
Color.RED; // 0 Color.GREEN; // 1 Color.BLUE; // 2
可以使用 values
来获得所有的枚举值,如:
List<Color> colors = Color.values;
若是在 switch
语句中使用枚举,那么需要处理枚举类型的所有值,或者定义一个 default
分支,否则会导致抛出一个警告:
Color someColor = Color.RED; switch (someColor) { case Color.RED: // ... break; case Color.GREEN: // ... break; // 会报错,因为没有对 Color.BLUE 进行处理 }
Dart中的枚举类型,有如下的限制:
- 无法继承枚举类型,无法使用mixin,无法实例化枚举
- 无法显示地初始化一个枚举类型
七、Mixins
Mixins
是一种在多类继承中重用一个类代码的手段,可以为类添加新的功能。使用 Mixins
的方法为使用 with
关键字,如下:
class Person { final name; Person(this.name); } class Program { program() => print('Program'); } class Reading { reading() => print('Reading'); } class Tom extends Person with Program, Reading { Tom(): super('Tom') { print('$name can:'); program(); reading(); } } main() { new Tom(); }
以上代码输出:
Tom can: Program Reading
如果一个类继承 Object
,但是该类没有构造函数,那么就不能调用 super
,这个类就是一个 mixin
,如:
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'); } } }
从 Dart 1.13
开始,Dart中的 Mixins
以下限制不再那么严格了:
-
Mixins
可以调用其他类,不再限制为继承Object
-
Mixin
可以调用super()
八、静态变量与静态函数
可以使用 static
关键字来定义 静态变量
和 静态函数
,他们属于类自身,不属于任意一个实例。如下:
class Chinese { static const from = 'China'; static whereAreYouFrom() { print('I am from $from'); } }
需要注意的是:
-
静态方法
由于属于类
,但是this
代指的是实例对象,所以静态方法不能访问this
-
静态方法
可以访问其他静态方法
和静态变量
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 【每日笔记】【Go学习笔记】2019-01-04 Codis笔记
- 【每日笔记】【Go学习笔记】2019-01-02 Codis笔记
- 【每日笔记】【Go学习笔记】2019-01-07 Codis笔记
- Golang学习笔记-调度器学习
- Vue学习笔记(二)------axios学习
- 算法/NLP/深度学习/机器学习面试笔记
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
数据驱动:从方法到实践
桑文锋 / 电子工业出版社 / 2018-3 / 49
本书是从理论到实践的全面且细致的企业数据驱动指南,从作者的百度大数据工作说起,完整还原其从零到一构建百度用户行为大数据处理平台经历。详解大数据本质、理念与现状,围绕数据驱动四环节——采集、建模、分析、指标,深入浅出地讲述企业如何将数据驱动方案落地,并指出数据驱动的价值在于“数据驱动决策”、“数据驱动产品智能”。最后通过互联网金融、电子商务、企业服务、零售四大行业实践,从需求梳理、事件指标设计、数据......一起来看看 《数据驱动:从方法到实践》 这本书的介绍吧!