一、Dart 语言
Dart 是一门面向对象的语言。
Dart 语言中所有的变量引用都是一个对象,并且所有的对象都对应一个类的实例。无论是数字、函数和null
,都是一个对象,所有对象继承自Object
类。
在 Dart 2.13 及以上版本,如果开启空安全(sound null safety)那么除了null
之外的所有类都继承自 Object
。
Object
Object 具有下列特性:
- 两个属性:
hashCode
和runtimeType
; - 两个方法:
noSuchMethod
和toString
; - 操作符:Operator
hashCode
hashCode 是 read-only 属性的 int 类型数据,因为 Dart 所有的变量引用均继承于Object
,所以每个变量引用都有自己的hashCode
。hashCode
的设计目的是为了提高 Dart VM 引擎在解析数据结构时的效率,并且参与判断对象的逻辑运算,与操作符 operator 相辅相成。考察如下代码:
void main() {
P p1 = P("yo","no");
P p2 = P("yo","no");
print(p1 == p2);
print(p1.hashCode);
print(p2.hashCode);
Map<P, String> myMap = {};
myMap[p1] = "first Item";
print(myMap.containsKey(p2));
}
class P {
var naam;
var kaam;
P(this.naam, this.kaam);
@override
int get hashCode {
int result = 11 * naam.hashCode;
result = 13 * (result + kaam.hashCode);
return result;
}
@override
bool operator == (dynamic otherObj) {
return (otherObj is P) && (otherObj.naam == naam) && (otherObj.kaam == kaam);
}
}
代码重写了P
对象的比较相等的逻辑,从而令p1
和p2
的比较结果返回为true
。但同时需要重写hashCode
的逻辑,否则返回值仍旧为false
。
Dart 建议hashCode
和Operator
如有必要重写其中之一,那么另一个也需要重写,从而令哈希映射正常工作,否则Operator
重写无效。
runtimeType
runtimeType
是 read-only 属性,表达数据结构在运行时的类型。runtimeType
涉及 Dart VM 运行机制,此处留坑以后再来填。
noSuchMethod
当调用对象上不存在的方法时,就会触发noSuchMethod
,考察如下代码:
void main() {
P p = P();
print(p.add(888));
}
class P {
@override
noSuchMethod(Invocation invocation) =>
'Got the ${invocation.memberName} with arguments ${invocation.positionalArguments}';
}
在 DartPad 编辑器运行上述代码,发现并未触发noSuchMethod
方法, 而是直接抛出了编译错误。实际noSuchMethod
触发需要有两个条件:
- 调用方必须是
dynamic
类型; - 调用方具有被调用方法的定义但未实现,同时
noSuchMethod
也被重写;
所以只有如下两种情况,才会触发noSuchMethod
:
void main() {
dynamic p = P();
Q q = Q(); // 该情况下,q 可以是 Q 类型,也可以是 dynamic
print(p.add(123));
print(q.add(123));
}
class P {
@override
noSuchMethod(Invocation invocation) => 'Got the ${invocation.memberName} with arguments ${invocation.positionalArguments}';
}
class Q {
miss(int data);
@override
noSuchMethod(Invocation invocation) => 'Got the ${invocation.memberName} with arguments ${invocation.positionalArguments}';
}
toString
toString
方法使用字符串来表达对象的信息,也可以在将数字转换为字符串的场景下使用。开发者也可以重写 toString
方法,以加入自定义内容。
void main() {
P p = P();
p.toString();
}
class P {
@override
toString() {
return 'this is custom text'
}
}
二、Dart 类与基本方法
1.类的定义和构造函数
class P {
String name;
int age;
P(String dataName, int dataAge) {
name = dataName;
age = dataAge;
}
bool isOld() {
return age > 30 ? true : false;
}
}
void main() {
var p = new P('tom', 12);
print(p.name);
}
上述代码中,声明了P
类,P 中含有两个属性:name
和age
。同时也声明了构造函数,通过向构造函数传入参数从而创建实例p
。
创建实例仍然使用传统的new
方法,但在 Dart 2 以上版本,new
关键字可以省略,同时构造函数也可以用语法糖简化,代码写法如下:
class P {
String name;
int age;
P(this.name, this.age);
bool isOld() {
return age > 30 ? true : false;
}
}
void main() {
var p = P('tom', 12);
print(p.name); // tom
}
命名构造函数
除默认的构造函数外,Dart 提供命名构造函数方法。代码如下:
class P {
String name;
int age;
P(this.name, this.age);
P.init(String dataName, int dataAge) {
name = dataName;
age = dataAge;
}
bool isOld() {
return age > 30 ? true : false;
}
}
void main() {
var p = P.init('tom', 12);
print(p.name); // tom
}
命名构造函数的功能看起来与默认构造函数的功能类似,那设计该机制的目的是什么呢?原因是 Dart 不支持构造函数的重载,无法使用不同的参数来执行构造方法,所以提供命名构造函数的机制来实现多方式创建实例。
工厂构造函数
除了上述两种构造函数外,Dart 还提供第三种构造函数:工厂构造函数。它的使用场景是:如果调用构造函数时,如果实例已存在,不会重新创建实例,而是使用已存在的实例,保证环境内只有一个实例存在,也就是单例模式。考察如下代码:
class P {
String name;
static Map<String, dynamic> _cache = {};
factory P(String name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
final p = P._internal(name);
_cache[name] = p;
return p;
}
}
P._internal(this.name);
}
void main() {
final p = P('tom');
print(p.name);
}
工厂构造函数不允许访问this
,所以_cache
的形态必须是static
。每次创建实例时,如果实例已经在_cache
中存在,那么返回已存在的实例。换句话说,工厂构造函数并没有自动创建实例,而是把决定权交给开发者。
2.类的属性和方法
静态变量和静态方法
和大多数语言一样,Dart 提供静态变量和静态方法:
class P {
static age;
static bool isOld() {
return age > 30 ? true : false;
}
}
静态方法无法使用this
,也不能访问非静态成员,类的实例也无法调用静态方法,并且静态变量只有在被使用的时候才会初始化。
私有属性和私有方法
Dart 不提供类似public
、protected
等关键字,在变量前添加下划线即可声明私有:
class P {
int _age;
P(this._age);
bool _isOld() {
return this._age > 30 ? true : false;
}
bool isOld() {
return _isOld();
}
}
void main() {
final p = P(20);
print(p._age); // error
print(p._isOld()); // error
print(p.isOld()); // false
}
实例无法直接调用私有方法,但是可以通过调用公有方法的形式间接调用私有方法。
3.类的继承
构造函数
Dart 通过extends
关键字实现继承,子类继承父类中公有的属性和方法,不会继承构造函数,所以子类的构造函数需通过super
关键字来调用或改造父类的构造函数:
class P {
num name;
num age;
P(this.name, this.age);
P.xxx(this.name, this.age); }
// class Q extends P {
// Q(num name, num age): super(name, age);
// }
// class Q extends P {
// num sex;
// Q(num sex, num name, num age): super.xxx(name, age) {
// this.sex = sex;
// }
// }
class Q extends P {
num sex;
Q(num sex, num name, num age): super(name, age) {
this.sex = sex;
}
}
void main() {
final q = Q(12, 13, 14);
print(q.sex); // 12
}
上述代码演示了子类中三种构造函数的表现:
- 直接复用父类构造函数
- 复用和改造父类默认构造函数
- 复用和改造父类命名构造函数
子类调用父类方法
如果子类需要调用父类方法,同样使用super
关键字,此时方法内部的this
指向子类:
class P {
num name;
num age;
P(this.name, this.age);
bool childCheck() {
return age > 20 ? true : false;
}
}
class Q extends P {
Q(num name, num age): super(name, age);
bool check() {
return super.check();
}
}
void main() {
final a = Q(12, 13);
print(a.childCheck()); // false
}
子类重写父类方法
class P {
num name;
num age;
P(this.name, this.age);
bool check() {
return age > 20 ? true : false;
}
}
class Q extends P {
Q(num name, num age): super(name, age);
@override
bool childCheck() {
return age == 13 ? true : false;
}
}
void main() {
final a = Q(12, 13);
print(a.childCheck()); // true
}
子类在重写父类方法时,只要方法名称与父类相同,便可实现逻辑重写,@override
为可选项,对于复杂逻辑的类功能建议添加。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!