第7章 Dart面向对象 -- 类和函数

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 2018.11.14日,在家附近的琴行报名了古典吉他班,一对二教学。 我是跟一个初二的小男孩一起上课,他是民谣吉他...
    雨听夜说阅读 1,128评论 0 6
  • 我站在桥上不是为了看风景, 我想登上生命之上看见生命。 为了看见生命的丑陋与卑微, 我又不得不低头看见自己。 我说...
    宁民阅读 256评论 0 6
  • 1、vi 字符串查找(常用):/要查找的字符串 敲enter开始查找 n代表下一个 N代表上一个从上往下找...
    跟我念一遍阅读 211评论 0 1