Dart是一种面向对象的语言,具有类和基于mixin(混入)的继承。每个对象都是一个类的实例, 与Java语言的继承体系类似,Dart中的每个类(除了Object)都只能有一个超类,但是类体可以在多个类层次结构中重用。
1. 类
1.1 普通类
1.1.1 普通成员
Dart使用class关键字表示一个类,对象具有由函数
和数据
(可以理解为Java语言的方法
和属性
)组成的成员。
与Java语言类似的是使用new XXX()方式创建类的对象,然后使用“.”或“?.”访问对象的函数或数据。
示例代码:
main() {
var p1 = new Person();
var p2 = new Person();
p1.num = 10;
p1.name = "赵四";
p2.num = 20;
p2.name = "刘能";
p1.display();
p2.display();
}
class Person{
int num;
String name;
void display(){
print("编号:${num}, 名字:${name}");
}
}
运行结果:
如果Person类定义在其他dart文件中,需要在main方法前引入其他dart文件。
import 'person.dart';
1.1.2 静态static
与Java中的静态static关键字类似,Dart语言中的static表达了“类成员”的含义,即这个成员不属于某个对象,而是属于整个类。
示例代码:将1.1.1示例中的num数据更改为“类数据”
main() {
var p1 = new Person();
var p2 = new Person();
Person.num = 10;
p1.name = "赵四";
Person.num = 20;
p2.name = "刘能";
p1.display();
p2.display();
}
class Person{
static int num;
String name;
void display(){
print("编号:${num}, 名字:${name}");
}
}
运行结果:
可以看到Person类的num属性通过类进行控制,不能通过对象控制。,Dart语言中对于“静态”方式调用的约束和理解与Java相似。
- 静态函数只能访问类中其他的静态成员,不能访问普通的成员。(静态函数只能访问静态函数和静态数据)
- 非静态函数可以访问类中的任意其他成员(非静态函数可以访问静态函数和静态数据)
- 类的外部只能通过类名.xxx的方式访问静态资源,不能使用对象.xxx的方式访问静态资源。(Java语言允许对象访问静态资源,Dart语言不允许这样做)
1.2 可调用类(Callable Class)
Dart语言中允许以使用函数的方式来调用类,必须通过在类中实现call()方法完成将类模拟成函数的操作
示例代码
main(){
var c = new Cxtest();
var x = c(2,3);
print(x);
}
class Cxtest{
call(dynamic a, dynamic b){
return a+b;
}
}
运行结果:5
1.3 枚举(enum)
与Java中枚举的使用方式类似
声明枚举
enum Color{red, green, blue, yellow, white, black}
使用枚举
var c = Color.blue;
遍历枚举
List<Color> list = Color.values;
list.forEach((Color c) => print("颜色:${c}"));
1.4 泛型编程(类型参数化)
Dart中的泛型使用方式与Java语言类似,使用泛型带来编程中的许多好处,此处不再累述(详见本人Java文章:《第16章 集合类》第4小节-泛型)。
在Dart中也可以向Java一样使用泛型编程,这种形式可以理解为将输入的类型参数化。
泛型声明可以使用在
- 类
- 函数参数
- 函数返回值
演示示例
main(){
DataUtil<int> d = new DataUtil();
d.show(32);
}
class DataUtil<T>{
void show(T a) => print("数据:${a}");
}
此处也可以像Java语言一样使用T extends num
形式约束泛型只能输入num或num类型的子类。详细用法请参照《第8章 Dart面向对象 -- 类的继承与混入(mixin)》)的第6节。
2. 函数
2.1 普通函数
Dart是一种纯粹的面向对象的语言,所以即使是函数也是对象。类似Javascript语言,Dart中的函数属于Function
类型。可以通过函数指定变量或者把函数作为其他函数的参数。main函数是程序入口。
Dart语言中函数结构与Java语言中的方法类似
返回值类型 函数名(参数列表){
函数体代码
return 返回值;
}
演示示例:两个数字相加的方法,并返回相加结果
main() {
var c = addition(3, 2.14);
print(c);
}
num addition(num a, num b){
return a+b;
}
可以使用=>
表示函数内容
比如
void main(){
runApp(new MyApp());
}
等价于
void main() => runApp(new MyApp());
如果某个函数的返回值类型是void
,可以省略void
关键字
比如
void main(){}
等价于
main(){}
2.2 构造函数
构造函数是在创建Dart类实例(对象)时进行调用的,可以参考Java的构造方法进行理解。
2.2.1 常规构造函数
与Java语言的语法类似,在Dart语言中如果没有显式声明构造函数,系统会提供默认无参的构造函数,但如果显式声明了构造函数,系统不再提供默认的无参构造函数。
构造函数不能声明返回值类型,函数名必须与类名一致
类名(参数列表){
}
演示示例
void main(){
var a = new NumberTest(2, 3);
a.display();
}
class NumberTest{
num a;
num b;
NumberTest(num a, num b){
this.a = a;
this.b = b;
}
void display(){
print("数据:${a},${b}");
}
}
上面示例的构造函数也可以写成下面这种形式,效果是一样的。
NumberTest(this.a, this.b){}
2.2.2 命名构造函数
Dart的语法不支持函数重载,无法像Java语言一样使用构造方法重载。因此当需要使用多个构造函数时,需要使用命名构造函数这种形式。
演示示例
void main(){
var a = new NumberTest(2,3);
var b = new NumberTest.cons1();
var c = new NumberTest.cons2(5);
a.display();
b.display();
c.display();
}
class NumberTest{
num a;
num b;
NumberTest(this.a, this.b){} //构造函数
NumberTest.cons1(){//命名构造函数1
this.a = 0;
this.b = 0;
}
NumberTest.cons2(this.a){//命名构造函数2
this.a = a;
this.b = 0;
}
void display(){
print("数据:${this.a},${this.b}");
}
}
运行结果
2.2.3 构造函数之间的调用
当需要构造函数之间互相调用时需要使用this
关键字
演示示例
void main(){
var a = new NumberTest(2,3);
var b = new NumberTest.cons1();
var c = new NumberTest.cons2(5);
a.display();
b.display();
c.display();
}
class NumberTest{
num a;
num b;
NumberTest(this.a, this.b){} //构造函数
NumberTest.cons1() : this(0,0); //命名构造函数1
NumberTest.cons2(num a) : this(a, 0); //命名构造函数2
void display(){
print("数据:${this.a},${this.b}");
}
}
2.2.4 工厂构造函数
通过使用factory
声明工厂构造函数,控制对象实例的数量。
语法
factory 类名(参数列表){
}
工厂函数中不能使用this关键字,有时需要借助命名构造函数完成功能
演示示例
void main(){
var a = FactoryTest("haha");
var b = FactoryTest("haha");
var c = FactoryTest("xixi");
print(a.hashCode);
print(b.hashCode);
print(c.hashCode);
print(a == b);
}
class FactoryTest{
String _name;
static Map<String, FactoryTest> cache_map = new Map();
factory FactoryTest(String name){ //工厂构造函数
if(cache_map.containsKey(name)){
return cache_map[name];
}else{
FactoryTest temp = FactoryTest.createInstance(name);
cache_map[name] = temp;
return temp;
}
}
FactoryTest.createInstance(this._name);//命名构造函数
void test(){
print(_name);
}
}
运行结果
2.3 可选参数函数
Dart语言支持像Javascript中函数传递json的方式一样使用可选参数函数
声明语法
返回值类型 函数名({参数1,参数2...参数n})
演示示例
void main(){
display1(1, "haha", 3.14); //必须补足实参
display2(a:1, c:3.14); //选择实参传入,此时只提供了a和c的实参
}
void display1(dynamic a, dynamic b, dynamic c){
print("a:${a},b:${b},c:${c}");
}
void display2({dynamic a, dynamic b, dynamic c}){
print("a:${a},b:${b},c:${c}");
}
运行结果
可以为可选参数函数设置参数的默认值
void display2({dynamic a, dynamic b = 0, dynamic c}){
print("a:${a},b:${b},c:${c}");
}
上面的代码将参数b的默认值设置为0,再次调用display2函数会得到如下结果:
2.4 函数参数和匿名函数
Dart语言中函数被认为是一种类型Function
,所以也可以将函数作为另一个函数的参数使用,此时类似Javascript语言中的函数参数的使用方式。
2.4.1 函数参数
演示示例:使用函数haha作为回调函数的参数
void main(){
doSomeThing(3, haha);
}
void doSomeThing(num a, Function callback){
print("do sth.${a} times");
callback();
}
void haha(){
print("done, good job");
}
运行结果
2.4.2 匿名函数
如果不想显示声明haha函数,可以通过声明匿名函数的方式来完成函数参数的实现
语法
(){
//功能代码
}
演示示例:使用匿名函数
void main(){
doSomeThing(3, (){
print("done, good job");
});
}
void doSomeThing(num a, Function callback){
print("do sth.${a} times");
callback();
}
如果只有一行代码也可以写成
doSomeThing(3, () => print("done, good job"));
2.5 闭包
一个函数的作用范围为其函数体的{}
内。当函数定义和函数表达式位于另一个函数的函数体内,内部函数可以访问它们所在的外部函数中声明的所有局部变量、参数和声明的其他内部函数。当其中一个这样的内部函数在包含它们的外部函数之外被调用时,就会形成闭包。
演示示例
void main(){
var r = 2;
var getCircleArea = (){
const PI = 3.1415926536;
return PI*r*r; //匿名方法可以访问外部函数的数据r,形成闭包。
};
print(getCircleArea());
}
2.6 等价函数
由于函数可以被当做变量看待,那么当多个变量指向同一个函数时,它们之间就可以理解为互为等价函数
演示示例
void topMethod(){}
class Test{
static void staticMethod(){}
void instanceMethod(){}
}
void main(){
dynamic temp1 = topMethod;
dynamic temp2 = Test.staticMethod;
dynamic t1 = new Test();
dynamic t2 = new Test();
dynamic temp3 = t1.instanceMethod;
print("顶级函数比较结果: ${temp1 == topMethod}");
print("静态函数比较结果: ${temp2 == Test.staticMethod}");
print("实例函数比较结果1: ${temp3 == t1.instanceMethod}");
print("实例函数比较结果2: ${temp3 == t2.instanceMethod}");
}
运行结果
2.7 初始化列表
可以在构造函数体运行之前初始化实例变量,用逗号分隔初始化。
演示示例
main (){
new NumberTest(2, 3).display();
}
class NumberTest{
num x;
num y;
num z;
NumberTest(this.x, this.y)
: z=x+y{} //带有初始化列表的构造方法,方法体内没有写代码
void display(){
print(z);
}
}
运行结果:5