Dart基础


Dart诞生于2011年10月10日,是一种“结构化的Web编程”语言,应用于Web、服务器、移动应用等领域的开发。

1 概念

  • Dart程序有统一的程序入口,即main()函数入口,与Java、C或C++语言相似。
// 入口方法
// main() {
//   print("入口方法");
// }

// 表示main方法没有返回值
void main() {
  // 打印信息
  print('入口方法');
}
  • 一切皆对象,所有对象都是类的实例,所有对象都继承自内置的Object类,类似于Java语言。
  • Dart语言是弱数据类型,指定类型非必须,指定数据类型和编译时的常量,可提高运行速度。
  • Dart没有public、protected和private的概念,私有特性通过变量或函数加上下划线来表示。
  • Dart工具可以检查出warning警告信息和errors错误信息。
    • 警告信息表明代码可能不工作,但是不会妨碍程序运行。
    • 错误信息可能是编译时的错误,也可能是运行时的错误。
    • 编译时的错误将阻止程序运行,运行时的错误会以exception异常方式呈现。
// 注释方法一
/* 注释方法二 */
///注释方法三
main() {
  print('注释方法');
}

1-1 关键字

  • 限制类保留字:await、yield。
  • 上下文关键字:async、hide、on、show、sync。
  • 保留字
    • assert、break、case、default、class、throw、true、new、continue、var。
    • switch、catch、else、extends、false、final、enum、for、null、finally、if。
    • return、super、void、rethrow、while、const、this、try、with、do、is、in。
  • 内置标识符
    • abstract、get、covariant、deferred、dynamic、factory、export、mixin、as、Function。
    • operator、set、interface、external、typedef、library、import、static、part、implements。

1-2 Dart中的库

  • 自定义的库:import 'lib/xxx.dart';
  • 系统内置库:import 'dart:convert';
    • dart:io:文件读写I/O相关操作的库。
    • dart:svg:事件和动画的矢量图支持。
    • dart:core:内建类型、对象及核心功能。
    • dart:html:网页开发使用到的库,使用频率高。
    • dart:math:数字常量及函数,提供随机数算法。
    • dart:async:异步编程支持,提供Future和Stream类。
    • dart:convert:不同类型之间的字符编码、解码支持。
    • dart:collection:对dart:core内置库提供更多的集合支持。
  • Pub包管理系统中的第三方库:https://pub.dev/
import 'dart:math';

main() {
  print('最小值:' + min(25, 27).toString());
  print('最大值:' + max(25, 27).toString());
}

(1) 实现请求数据

import 'dart:io';
import 'dart:convert';

void main() async {
  var result = await getDataFromAPI();
  print(result);
}

// API接口:http://news-at.zhihu.com/api/3/stories/latest
getDataFromAPI() async {
  // 创建HTTPClient对象
  var httpClient = new HttpClient();
  // 创建URI对象
  var uri = new Uri.http('news-at.zhihu.com', '/api/3/stories/latest');
  // 发起请求,等待响应
  var request = await httpClient.getUrl(uri);
  // 关闭请求,等待响应
  var response = await request.close();
  // 解码响应的内容
  return await response.transform(utf8.decoder).join();
}

(2) async和await

  • 只有async方法才能使用await关键字调用方法。
  • 如果调用别的async方法必须使用await关键字。
  • async是让方法变成异步,await是等待异步方法执行完成。
void main() async {
  var result = await testAsync();
  print(result);
}

// 异步方法
testAsync() async {
  return '异步方法';
}

(3) 导入第三方库

  • 项目根目录下新建一个pubspec.yaml文件,在文件中配置名称、描述、依赖等信息。
  • 命令窗口进入pubspec.yaml文件路径下,输入dart pub get回车获取包下载到本地。
name: xxx
description: pubtest

environment:
  sdk: '>=2.10.0 <3.0.0'

dependencies:
  http: ^0.13.5
  date_format: ^2.0.7
  • 项目中引入库需使用import命令,例如:import 'package:http/http.dart' as http;
import 'dart:convert' as convert;
import 'package:http/http.dart' as http;
import 'package:date_format/date_format.dart';

main() async {
  var url = Uri.http('news-at.zhihu.com', '/api/3/stories/latest');
  // 等待HTTP响应,然后对Json格式的响应进行解码
  var response = await http.get(url);
  if (response.statusCode == 200) {
    var jsonResponse = convert.jsonDecode(response.body);
    print(jsonResponse);
  } else {
    print('请求失败,状态为:${response.statusCode}');
  }
  print(formatDate(DateTime(2023, 01, 10), [yyyy, '-', mm, '-', dd]));
}

(4) 库的冲突问题

  • 创建一个lib库,该库目录中的Person1.dart文件内容如下。
class Person {
  String name;
  int age;
  // 默认构造函数的简写
  Person(this.name, this.age);

  Person.setInfo(String name, int age) {
    this.name = name;
    this.age = age;
  }

  void printInfo() {
    print('Person1:${this.name}---${this.age}');
  }
}
  • lib库目录中的Person2.dart文件内容如下。
class Person {
  String name;
  int age;
  // 默认构造函数的简写
  Person(this.name, this.age);

  Person.setInfo(String name, int age) {
    this.name = name;
    this.age = age;
  }

  void printInfo() {
    print('Person2:${this.name}---${this.age}');
  }
}
  • Person1.dartPerson2.dart中都有Person类(重名了),重命名其中一个库。
import 'lib/Person1.dart';
import 'lib/Person2.dart' as lib;

main() {
  Person p1 = new Person('张三', 21);
  p1.printInfo();
  lib.Person p2 = new lib.Person('李四', 22);
  p2.printInfo();
}

(5) 引入部分功能

  • lib库目录中新建一个MyMath.dart文件,内容如下。
void getName() {
  print('张三');
}

void getAge() {
  print(21);
}

void getSex() {
  print('男');
}
  • 使用hide关键字隐藏库中不需要的部分,show关键字仅导入库中需要的部分。
// 隐藏不需要的部分,使用hide关键字
import 'lib/MyMath.dart' hide getAge;
// 只导入需要的部分,使用show关键字
import 'lib/MyMath.dart' show getName;

void main() {
  getName();
  // 已隐藏,会报错
  // getAge();
}

(6) 延迟加载(懒加载)*

  • 懒加载:可以在需要时再进行加载,最大好处即减少APP的启动时间,使用时用loadLibrary()方法加载。
  • 使用deferred as关键字来指定,例如:import 'package:deferred/hello.dart' deferred as hello;
// 好像没这个库,引入会报错
import 'package:deferred/hello.dart' deferred as hello;

greet() async {
  await hello.loadLibrary();
  hello.printGreeting();
}

1-3 变量与常量

  • Dart是脚本类语言,可以不预先定义变量类型,它会自动推导类型。
  • 可以使用var关键字来声明变量,也可以通过类型关键字来定义变量。
  • 变量命名规则
    • 变量名必须由数字、字母、下划线_和美元符$组成。
    • 标识符开头不能是数字,标识符不能是保留字和关键字。
    • 变量名区分大小写,尽量见名知义,变量名用名词,方法名用动词。
main() {
  // 使用var关键字定义变量
  var str1 = 'var关键字定义变量';
  print(str1);
  var num1 = 123;
  print('var关键字定义变量:$num1');

  // 通过类型关键字定义变量
  String str2 = '类型关键字定义变量';
  print(str2);
  int num2 = 12345;
  print('类型关键字定义变量:$num2');
}
  • 常量:定义的变量不变,使用final或const关键字定义。
  • final是惰性初始化,即在运行时第一次使用前才初始化。
main() {
  String str = '变量值1';
  // 变量可以改变值
  str = '变量值2';
  print('定义变量:$str');

  // const关键字定义常量
  const PI1 = 3.14159;
  // 常量不可以改变值,改变值将报错
  // PI1 = 3.14159267;
  print('const关键字定义常量:$PI1');

  // final关键字定义常量
  final PI2 = 3.14159;
  // PI2 = 3.14159253;
  print('final关键字定义常量:$PI2');
}
  • final的值只能被设定一次,是一个运行时常量,const则是一个编译时常量。
main() {
  // final关键字是运行时常量
  final time1 = new DateTime.now();
  print(time1);
  
  // const关键字是编译时常量
  // const time2 = new DateTime.now();
  // print(time2);
}

2 运算符

  • 位运算符:&(与)、|(或)、^(异或)、~expr(一元位补码)、<<(左移)、>>(右移)。
  • 算术运算符:+-*/%~/(返回一整数值的除法)、-expr(一元减号即负号)。
  • 关系运算符:==(等)、!=(不等)、>(大于)、<(小于)、>=(大于等于)、<=(小于等于)。
  • 赋值运算符:=??=+=-=*=/=%=~/=<<=>>=&=^=|=
  • 逻辑运算符:!expr(反转表达式)、||(逻辑或)、&&(逻辑与),只两个值。
  • 级联运算符:..(可对同一对象执行系列操作,严格说级联符号并非运算符)。
  • 条件表达式:condition ? expr1 : expr2(三元表达式)、expr1 ?? expr2
  • 类型判定运算符:is(相应类型)、is!(非相应类型)、as(转换为指定类型)。

2-1 位运算符

  • ^(异或):两个输入相同时为0,不同为1。
  • 都是通过操作位的移动来达到运算的目的。
main() {
  // 位运算符
  final v = 0x77; // 16进制,10进制为119,2进制为0111 0111
  final b = 0xee; // 16进制,10进制为238,2进制为1110 1110
  print((v & b).toRadixString(2));
  print((v | b).toRadixString(2));
  print((v ^ b).toRadixString(2));
  print((~ v).toRadixString(2));
  print((v << 1).toRadixString(2));
  print((v >> 2).toRadixString(2));
  print((v &~ b).toRadixString(2)); // 与非&~,转为2进制
}

2-2 算术运算符

main() {
  // 算术运算符
  var a = 3;
  var b = 6;
  var c = 9;
  print('加:' + (a + c).toString());
  print('减:' + (a - b).toString());
  print('乘:' + (a * b).toString());
  print('除:' + (c / b).toString());
  print('取余:' + (c % b).toString());
  print('取整:' + (c ~/ b).toString());

  var d = b - a;
  print('赋值:' + d.toString());
}
  • 还支持前缀递增++var、前缀递减--var、后缀递增var++、后缀递减var--运算符。
main() {
  var a = 3;
  var x1, x2, y1, y2;
  // 在x1获得值前自增a
  x1 = ++a;
  print('$a --- $x1');
  // 在y1获得值后自增a
  y1 = a++;
  print('$a --- $y1');
  // 在x2获得值前自减a
  x2 = --a;
  print('$a --- $x2');
  // 在y2获得值后自减a
  y2 = a--;
  print('$a --- $y2');
}

2-3 关系运算符

  • 测试两个对象是否表示相同的事务,使用==运算符。
main() {
  // 关系运算符
  int a = 5;
  int b = 3;
  print(a != b);
  print(a > b);
  print(a >= b);
  print(a == b);
  print(a < b);
  print(a <= b);

  // 用于条件判断
  if(a > b) {
    print('a大于b');
  } else {
    print('a小于b');
  }
}
  • 确定两个对象是否完全相同,则用identical()函数。
main() {
  int a = 5;
  // 判断对象是否完全相同
  int b = 5;
  print(identical(a, b));
}

2-4 赋值运算符

  • 使用=运算符从右向左进行赋值操作,当变量为空时,可以使用??=运算符进行赋值。
main() {
  // 赋值运算符,从右向左
  var v = 1;
  print(v);

  // 如果z为空,则将值分配给z,否则z保持不变
  var z;
  z ??= v;
  print(z);
}
  • 复合赋值运算符:+=-=*=/=%=~/=<<=>>=&=^=|=
main() {
  // 复合赋值运算符
  var a = 12;
  a += 10;
  print(a);

  var b = 4;
  b *= 3;
  print(b);
}

2-5 逻辑运算符

main() {
  // 逻辑运算符
  bool flag1 = true;
  print(!flag1);

  // 逻辑或||
  bool flag2 = false;
  bool flag3 = false;
  print(flag2 || flag3);
  
  // 逻辑与&&
  print(flag1 && flag2);
}
  • 逻辑运算符也可以反转布尔表达式,或组合布尔表达式。
main() {
  // 组合表达式
  int age = 21;
  String sex = '男';
  if(age == 23 && sex == '男') {
    print('$age --- $sex');
  } else {
    print('不打印');
  }
}

2-6 级联运算符

  • 使用..表示,可对同一对象执行一系列操作,严格说级联的双点符号并非运算符。
  • 只是Dart语法的一部分,类似于JavaScript中Promise的then处理,目的是简化代码。
class Person {
  String name;
  num age;
  Person(this.name, this.age);
  void printInfo() {
    print('${this.name} --- ${this.age}');
  }
}

main() {
  Person p = new Person('张三', 21);
  p.printInfo();

  p.name = '李四';
  p.age = 22;
  p.printInfo();
  
  p
    ..name = '王五'
    ..age = 23
    ..printInfo();
}

2-7 条件表达式

  • condition ? expr1 : expr2:条件为真返回expr1,否则返回expr2(三元表达式)。
main() {
  // if...else
  bool flag1 = true;
  if(flag1) {
    print('正确');
  } else {
    print('错误');
  }
  
  // condition ? expr1 : expr2
  var flag2 = true;
  String result = flag2 ? '正确' : '错误';
  print(result);
}
  • expr1 ?? expr2:如果expr1为非空,返回expr1的值,否则计算并且返回expr2的值。
main() {
  // expr1 ?? expr2
  var x;
  var y = x ?? 10;
  print(y);
}

2-8 类型判定运算符

  • is:当对象是相应类型时,返回true。
  • is!:对象不是相应类型时,返回true。
  • as:将一个对象转换为指定类型,前提是能够转换,转换之前可先用is进行判断。
main() {
  // 类型判定运算符
  var a = 123;
  if(a is int) {
    print('a是int类型');
  } else {
    print('a是其他类型');
  }
}

3 数据类型

  • 常用的数据类型
    • 数字(Number)
      • int关键字(整型)
      • double关键字(浮点型)
    • 布尔(Boolean):bool关键字。
    • 字典(Map):键和值可以是任何类型的对象。
    • 数组(List):Dart中是列表对象,大多数人称之为列表。
    • 集合(Set):Dart中Set是一个元素唯一并且无序的集合。
    • 字符串(String):String关键字,开发过程中大量使用。
  • 不常用的数据类型
    • Runes:表示字符串中的UTF-32编码字符。
    • Symbol:表示在Dart程序中声明的运算符或标识符。

3-1 数字

  • int整型和double浮点型,都是Number类型的子类,int类型不能包含小数点。
  • 包含+-*/%以及位移操作等,常用的方法有abs、ceil和floor。
main() {
  // int,必须是整型
  int num1 = 123;
  print('整型:$num1');

  // double,既可以是整型,也可以是浮点型
  double num2 = 123;
  double num3 = 123.5;
  print('浮点型:$num2');
  print('浮点型:$num3');
  
  // 运算符操作
  var sum = num1 + num2;
  print('运算符操作:$sum');
}

3-2 布尔

  • 只有字面量true和false是布尔类型,这两个对象都是编译时常量。
main() {
  // 布尔类型
  bool flag1 = true;
  print('布尔类型:$flag1');
  bool flag2 = false;
  print('布尔类型:$flag2');
}
  • Dart是强布尔类型检查,只有bool类型的值是true才被认为是true。
main() {
  // 条件判断语句
  var flag = true;
  if(flag) {
    print('布尔类型:true');
  } else {
    print('布尔类型:false');
  }

  var a = 123;
  var b = 456;
  if(a == b) {
    print('条件判断:a == b');
  } else {
    print('条件判断:a != b');
  }
}

3-3 字典

  • 将key和value关联在一起,也就是键值对,且key必须是唯一的。
main() {
  // Map定义方式一
  var person1 = {'姓名': '张三', '年龄': 21, '工作': '程序员'};
  print('姓名:${person1['姓名']}');
  print('方式一:$person1');

  // Map定义方式二:构造函数Map()
  var person2 = new Map();
  person2['姓名'] = '李四';
  person2['年龄'] = 22;
  person2['工作'] = '设计师';
  print('方式二:$person2');
}
  • 可以为Map对象添加新的键值对,通过length获取键值对的数量。
main() {
  var person = {'姓名': '张三', '年龄': 21};
  // 添加新的键值对
  person['性别'] = '男';
  print(person);
  
  // 使用length获取键值对的数量
  print('length获取到的person键值对数量:${person.length}');
}
  • 常用属性:keys(获取key值)、values(获取value值)、isEmpty、isNotEmpty。
main() {
  // 常用属性
  Map person = {'姓名': '张三', '年龄': 21, '性别': '男'};
  print('键:${person.keys}');
  print('值:${person.values}');
  print('是否为空:${person.isEmpty}');
  print('是否不空:${person.isNotEmpty}');
}
  • 常用方法:remove()、addAll()(映射拼接)、containsValue(查看映射内的值)。
main() {
  // 常用方法
  Map person = {'姓名': '张三', '年龄': 21};
  person.addAll({'性别': '男'});
  print(person);
  person.remove('性别');
  print(person);
  print('是否包含“张三”:${person.containsValue('张三')}');
}

3-4 数组

  • 具有一系列相同类型的数据,类似于JavaScript的Array数组对象。
main() {
  // List定义方式一
  var l1 = ['张三', 21, true];
  print('List定义方式一:$l1');
  print('\tList的长度:${l1.length}');
  print('\tList的位置:${l1[0]}');

  // List定义方式二:指定类型
  var l2 = <int>[21, 23, 25, 27, 29];
  var l3 = <String>['张三', '李四', '王五', '赵六'];
  print('List定义方式二:$l2');
  print('List定义方式三:$l3');

  // List定义方式三:增加数据
  var l4 = [];
  print('List定义方式三:$l4');
  print('\tList的长度:${l4.length}');
  l4.add('孙七');
  l4.add('周八');
  l4.add('吴九');
  l4.add('郑十');
  l4.add(30);
  print('List定义方式三:$l4');
  print('\tList的长度:${l4.length}');

  // List定义方式四:新版中已去除,在Android Studio中仍然可用
  var l5 = new List();
  l5.add('张三');
  l5.add('李四');
  l5.add('王五');
  l5.add('赵六');
  l5.add('孙七');
  print('List定义方式四:$l5');
}
  • List对象的第一个元素索引是0,最后一个元素索引是list.length-1。
main() {
  // 创建一个固定长度的集合
  var l1 = List.filled(3, '');
  print('固定长度的集合:$l1');
  l1[0] = '张三';
  l1[1] = '李四';
  l1[2] = '王五';
  print('固定长度的集合:$l1');
  // 已固定长度,添加Unhandled exception报错
  // l1.add('赵六');
  // print('固定长度的集合:$l1');
  // 也无法通过修改长度进行改变
  // l1.length = 5;

  // 该定义方式可以改变集合的长度
  var l2 = ['孙七', '周八'];
  l2.length = 0;
  print('改变集合的长度:$l2');

  var l3 = List<String>.filled(3, '');
  l3[0] = '字典';
  l3[1] = '布尔';
  l3[2] = '数组';
  print('改变集合的长度:$l3');
}
  • 常用属性:length(长度)、reversed(翻转)、isEmpty(为空)、isNotEmpty(不为空)。
main() {
  // 常见属性
  var list = ['苹果', '西瓜'];
  list.add('葡萄');
  print('列表:$list');
  print('长度:${list.length}');
  print('不空:${list.isNotEmpty}');
  print('为空:${list.isEmpty}');
  // 翻转后再转换成List
  print('列表:${list.reversed}');
}
  • 常用方法
    • add(增加)、addAll(拼接数组)、remove(删除)、removeAt(删除)、fillRange(修改)。
    • indexOf:查找,若查找不到则返回-1。
    • insert(index, value):指定位置插入。
    • insertAll(index, list):指定位置插入List。
    • join():List转换成字符串。
    • split():字符串转换成List。
    • toList():其他类型转换成List。
main() {
  // 常用方法
  List l1 = ['桃子'];
  l1.add('香蕉');
  l1.addAll(['葡萄', '樱桃']);
  print('列表:$l1');
  // 查找不到则返回-1
  print('查找:${l1.indexOf('苹果')}'); 
  l1.remove('葡萄');
  print('删除“葡萄”:$l1');
  l1.removeAt(1);
  print('删除“香蕉”:$l1');
  // 将位置(1-2]之间的数据改为'西瓜'
  l1.fillRange(1, 2, '西瓜');
  print('替换“樱桃”:$l1');
  l1.insert(1, '杏子');
  print('插入:$l1');
  l1.insertAll(1, ['苹果', '龙眼']);
  print('插入:$l1');
  var str = l1.join('、');
  print('转换:$str');
  var l2 = str.split('、');
  print('转换:$l2');
}

3-5 集合

  • 最主要的功能就是去除数组中存在的重复内容。
  • 无顺序且不能重复的集合,不能通过索引获取值。
main() {
  // 给数组去重
  var s = new Set();
  s.add('苹果');
  s.add('苹果');
  s.add('苹果');
  print(s);
  print(s.toList());
}

3-6 字符串

  • 字符串类型,使用单引号、双引号或三引号定义,开发中大量使用。
main() {
  // 字符串定义的几种方式
  var str1 = '字符串定义方式一';
  var str2 = "字符串定义方式二";
  print(str1);
  print('$str2\n');

  String str3 = '字符串定义方式三';
  String str4 = "字符串定义方式四";
  print(str3);
  print('$str4\n');

  String str5 = '''
  字符串定义方式五
  字符串定义方式五
  ''';
  String str6 = """
  字符串定义方式六
  字符串定义方式六
  """;
  print(str5);
  print(str6);
}
  • 通过两种方式进行字符串的拼接:使用$符号,或+符号进行操作。
main() {
  // 字符串的拼接
  String str1 = '你好';
  String str2 = '张三';
  print('拼接一:$str1,$str2');
  print('拼接二:' + str1 + ',' + str2);
}

3-7 数据类型判断

  • 使用is关键字来判断数据类型。
main() {
  // is关键字判断数据类型
  var str = '12345';
  if(str is String) {
    print('String类型');
  } else if(str is int) {
    print('int类型');
  } else {
    print('除String和int的其他类型');
  }
}

3-8 数据类型转换

  • 使用parse()将String类型转换成Number类型,toString()将Number类型转换成String类型。
main() {
  // String转Number:parse()
  String str1 = '123';
  var myNum1 = int.parse(str1);
  print(myNum1 is int);

  String str2 = '123.3';
  var myNum2 = double.parse(str2);
  print(myNum2 is double);

  // Number转String:toString()
  var myNum3 = 123;
  var str3 = myNum3.toString();
  print(str3 is String);
}
  • 为了防止转换过程中报错,可以使用try…catch语句。
main() {
  // 防止报错,使用try...catch语句
  String price = '';
  try {
    var myNum = double.parse(price);
    print(myNum);
    print(myNum is double);
  } catch(err) {
    print(0);
  }
}
  • isEmpty可以用来判断字符串是否为空。
main() {
  // 其他类型转Boolean类型
  var str = 'Hi~';
  if(str.isEmpty) {
    print('为空');
  } else {
    print('不空');
  }

  var myNum1;
  if(myNum1 == null) {
    print('为空');
  } else {
    print('不空');
  }
  
  var myNum2 = 0/0;
  if(myNum2.isNaN) {
    print('非数');
  } else {
    print(myNum2);
  }
}

4 流程语句

  • if...else...语句、switch...case语句。
  • for循环、while循环、do...while循环。
  • break跳出一层循环、continue跳出本次循环。
  • forEachmapwhereanyevery循环。

4-1 if语句

main() {
  // if...else...语句
  String today = '星期一';
  if(today == '星期一') {
    print('今天星期一');
  } else {
    print('美好的一天');
  }
}

4-2 switch

main() {
  // switch...case语句
  String today = '星期一';
  switch(today) {
    case '星期一':
      print('今天星期一');
      break;
    default:
      print('美好的一天');
      break;
  }
}

4-3 for和while

  • for循环、while循环、do…while循环。
  • 第一次循环条件不成立的情况,while循环不执行,do…while循环执行一次。
main() {
  // for循环
  for(int x=1; x<=3; x++) {
    print('for循环:$x');
  }

  // while循环
  int y = 1;
  while(y <= 3) {
    print('while循环:$y');
    y++;
  }

  // do...while循环
  int z = 1;
  int sum = 0;
  do {
    sum += z;
    z++;
  } while(z <= 3);
  print('do...while循环的和:$sum');
}

4-4 跳出一层循环

  • break用于跳出循环,如果有多层循环,则只能跳出一层循环。
main() {
  // break跳出循环
  for(var i=1; i<=5; i++) {
    if(i == 4) {
      break;
    }
    print(i);
  }
}

4-5 跳出本次循环

  • continue用来跳出本次循环。
main() {
  // continue跳出本次循环
  for(var i=1; i<=3; i++) {
    if(i == 2) {
      continue;
    }
    print(i);
  }
}

4-6 其他循环语句

  • forEach:循环遍历List。
main() {
  List mylist = ['樱桃', '西瓜', '香蕉'];
  for(var i=0; i<mylist.length; i++) {
    print('方法一:${mylist[i]}');
  }

  for(var item in mylist) {
    print('方法二:$item');
  }
  
  // forEach
  mylist.forEach((value) {
    print('forEach:$value');
  });
}
  • map:用于修改集合中的数据。
main() {
  // map
  List mylist = [1, 2, 3];
  var doubleList = mylist.map((value) {
    return value * 2;
  });
  print(doubleList.toList());
}
  • where:返回满足条件的数据。
main() {
  // where
  List mylist = [1, 3, 5, 7, 9];
  var newList = mylist.where((value) {
    return value >= 5;
  });
  print(newList.toList());
}
  • any:只要集合中有满足条件的数据就返回true。
main() {
  // any
  List mylist = [1, 3, 5, 7, 9];
  var anyList = mylist.any((value) {
    return value >= 5;
  });
  print(anyList);
}
  • every:集合中的每个数据都满足条件才返回true,否则返回false。
main() {
  // every
  List mylist = [1, 3, 5, 7, 9];
  var everyList = mylist.every((value) {
    return value >= 5;
  });
  print(everyList);

  // 循环方法同样适用于set
  var s = new Set();
  s.addAll([11111, 22222, 33333]);
  s.forEach((value) => print(value));

  // 循环方法同样适用于map
  var person = {'姓名': '张三', '工作': '测试'};
  person.forEach((key, value) {
    print('$key --- $value');
  });
}

5 函数说明

  • 函数即方法,内置的方法,例如:print()
  • 自定义方法,格式:返回类型 方法名称(参数1, 参数2, ..., 参数n) { }
// 全局作用域
void printInfo() {
  print('全局作用域');
  printGlobal() {
    print('局部作用域');
  }

  // 局部作用域只在局部调用
  printGlobal();
}

void main() {
  print('内置方法');
  // 调用方法
  printInfo();

  // 全局作用域
  int getNum() {
    var myNum = 12345678;
    return myNum;
  }

  // 调用方法
  var n = getNum();
  print(n);
}

5-1 参数

main() {
  // 定义一个方法,求1到这个数的所有数之和
  int sumNum(int n) {
    var sum = 0;
    for(var i=1; i<=n; i++) {
      sum += i;
    }
    return sum;
  }

  var n = sumNum(10);
  print(n);

  // 定义一个方法,然后打印用户信息
  // 方法传参:形参
  String printUserInfo(String username, int age) {
    return '姓名:$username --- 年龄:$age';
  }

  // 方法传参:实参
  print(printUserInfo('张三', 21));
}

(1) 可选参数

main() {
  // 定义一个方法,该方法带可选参数:[Data type Variable]
  String printUserInfo(String username, [int age]) {
    if(age != null) {
      return '姓名:$username --- 年龄:$age';
    }
    return '姓名:$username --- 年龄:保密';
  }

  // 方法传参:实参
  print(printUserInfo('张三', 21));
}

(2) 默认参数

main() {
  // 定义一个方法,该方法带默认参数:[Data type Variable = 'Default']
  String printUserInfo(String username, [int age = 21]) {
    if(age != null) {
      return '姓名:$username --- 年龄:$age';
    }
    return '姓名:$username --- 年龄:保密';
  }

  // 方法传参:实参
  print(printUserInfo('张三'));
  print(printUserInfo('李四', 22));
}

(3) 命名参数

main() {
  // 定义一个方法,该方法带命名参数:{Data type Variable = 'Default'}
  String printUserInfo(String username, {int age}) {
    if(age != null) {
      return '姓名:$username --- 年龄:$age';
    }
    return '姓名:$username --- 年龄:保密';
  }

  // 方法传参:实参
  print(printUserInfo('张三', age: 21));

  // 实现一个方法,该方法将另一个方法当作参数传入
  fn1() {
    print('将fn1作为一个参数传入fn2中');
  }

  fn2(fName) {
    fName();
  }

  fn2(fn1);
}

5-2 箭头函数

main() {
  // 箭头函数:forEach打印List里的数据
  List list = ['樱桃', '香蕉', '苹果'];
  list.forEach((element) => print(element));
  list.forEach((element) => {print(element)});

  // 修改List里的数据,让数组中大于2的值乘以2
  List listNum = [4, 1, 2, 3, 4];
  var newList = listNum.map((e) {
    if(e > 2) {
      return e * 2;
    }
    return e;
  });
  print(newList.toList());

  var newListNum = listNum.map((e) => e > 2 ? e * 2 : e);
  print(newListNum.toList());
}

5-3 相互调用

main() {
  // 定义一个方法,使用isEvenNumber判断一个数是否为偶数
  bool isEvenNumber(int n) {
    if(n % 2 == 0) {
      return true;
    }
    return false;
  }

  // 定义一个方法,打印1-n以内的所有偶数
  printNum(int n) {
    for(var i=1; i<=n; i++) {
      if(isEvenNumber(i)) {
        print(i);
      }
    }
  }

  // 函数的相互调用
  printNum(9);
}

5-4 匿名函数

main() {
  // 匿名函数
  var printNumber = (){
    print(123);
  };
  printNumber();
}

5-5 方法递归

main() {
  // 方法递归:条件满足的情况下在方法内调用自身,求1-100的和
  var sum = 0;
  fn(int n) {
    sum += n;
    if(n == 1) {
      return;
    }
    fn(n - 1);
  }
  
  fn(100);
  print(sum);
}

5-6 闭包操作

  • 全局变量特点:常驻内存,污染全局。
  • 局部变量特点:不常驻内存,支持垃圾机制回收,不污染全局。
  • 要想实现常驻内存,又不会污染全局,闭包可以解决这些问题。
    • 写法:函数嵌套函数,并return里面的函数,这样就形成了闭包。
    • 说明:内部函数调用外部函数的变量或参数,且这些变量或参数不会被系统回收。
main() {
  // 闭包
  fn() {
    var a = 123;
    return() {
      a++;
      print(a);
    };
  }

  var b = fn();
  b();
  b();
  b();
}

5-7 自执行方法

main() {
  // 自执行方法:即不需要调用,自动执行的方法
  (() {
    print('自执行方法一');
  })();

  ((String n) {
    print(n);
  })('自执行方法二');
}

6 面向对象

  • 面向对象编程OOP的三个基本特征
  • 封装:把客观事物封装成抽象的类,并把自己的部分属性和方法提供给其他对象。
  • 继承:可使用现有类的功能,并在无需重新编写原来类的情况下对功能进行扩展。
  • 多态:允许将子类类型的指针赋值给父类类型的指针,同一个函数调用会有不同的执行效果。

6-1 类的创建

  • Dart中一切皆对象,所有的对象都继承自Object类,一个类通常由属性和方法组成。
// Person类的创建
class Person {
  // 类的属性
  String name = '张三';
  int age = 21;

  // 类的方法
  void getInfo() {
    print('$name --- $age');
    print('${this.name} --- ${this.age}');
  }

  void setInfo(int age) {
    this.age = age;
  }
}

void main() {
  // 实例化Person类
  var p1 = new Person();
  // 调用属性
  print(p1.name);
  // 调用方法
  p1.getInfo();

  Person p2 = new Person();
  // print(p2.name);
  p2.setInfo(22);
  p2.getInfo();
}

6-2 构造函数

  • Dart中的4种构造函数
  • 普通构造函数:ClassName(...),分为无参构造函数和有参构造函数。
  • 命名构造函数:ClassName.identifier(...),支持一个类实现多个构造函数,不能重载。
  • 常量构造函数:const ClassName(...),如果生成类的对象不变,可以定义常量构造函数。
  • 工厂构造函数:factory ClassName(...),factory关键字可以放在类名函数和命名函数前。

(1) 普通构造函数

  • 如果不声明构造函数,则Dart会提供一个默认的无参构造函数。
  • 若自定义有参构造函数,那么默认的无参构造函数就不存在了。
class Person {
  String name = '张三';
  int age = 21;

  // 构造函数,该方法在实例化时触发
  Person() {
    print('构造函数');
  }

  void printInfo() {
    print('${this.name} --- ${this.age}');
  }
}

void main() {
  // 实例化
  Person p = new Person();
  // p.printInfo();
}
  • 实现的功能:在构造函数中,可以动态地给类指定属性。
class Person {
  // 有参构造函数,提供语法糖简化
  String name;
  int age;

  // 自定义构造函数
  // Person(String name, int age) {
  //   this.name = name;
  //   this.age = age;
  // }

  // 构造函数的简写
  Person(this.name, this.age);

  void printInfo() {
    print('${this.name} --- ${this.age}');
  }
}

void main() {
  // 实例化
  Person p1 = new Person('张三', 21);
  p1.printInfo();

  Person p2 = new Person('李四', 22);
  p2.printInfo();
}

(2) 命名构造函数

class Person {
  String name;
  int age;

  // 命名构造函数
  Person.now() {
    print('命名构造函数');
  }

  Person.setInfo(String name, int age) {
    this.name = name;
    this.age = age;
  }

  void printInfo() {
    print('${this.name} --- ${this.age}');
  }
}

void main() {
  // 实例化DateTime调用它的命名构造函数
  var d = new DateTime.now();
  print(d);
  
  Person p1 = new Person.now();
  Person p2 = new Person.setInfo('张三', 21);
  p2.printInfo();
}

6-3 类模块化

  • 将创建的类放在单独的文件夹下,例如:Person类,放在/lib/Person.dart中。
class Person {
  String name;
  int age;
  
  Person(this.name, this.age);
  
  Person.now() {
    print('命名构造函数');
  }
  
  Person.setInfo(String name, int age) {
    this.name = name;
    this.age = age;
  }
  
  void printInfo() {
    print('${this.name} --- ${this.age}');
  }
}
  • 在另一个文件中调用该Person类,则需要导入该类的类文件名。
import './lib/Person.dart';

void main() {
  Person p = new Person.setInfo('张三', 21);
  p.printInfo();
}

(1) 私有属性

  • Dart中没有public、private protected等访问修饰符,但可使用_将属性和方法私有化。
  • 自定义私有属性,或自定义私有方法时,必须将其单独抽类到一个文件中,这样才有效果。
  • 例如:Animal类,定义name为私有属性,将创建的Animal类放在/lib/AnimalProperties.dart中。
class Animal {
  // 私有属性name
  String _name;
  int age;
  
  Animal(this._name, this.age);
  
  void printInfo() {
    print('${this._name} --- ${this.age}');
  }
  
  // 公有方法
  String getName() {
    // 私有属性name只在当前类中使用
    return this._name;
  }
}
  • name私有属性只在当前的Animal类中使用,此时main()函数无法访问Animal类中的私有属性name。
  • name私有属性在Animal类的getName()公有方法中,可通过访问公有方法间接地使用私有属性name。
import './lib/AnimalProperties.dart';

void main() {
  Animal a = new Animal('小狗', 3);
  // 无法访问私有属性
  // print(a._name);
  print(a.age);

  // 访问公有方法来访问私有属性
  print(a.getName());
}

(2) 私有方法

  • 例如:Animal类,定义run()为私有方法,将创建的Animal类放在/lib/AnimalMethods.dart中。
class Animal {
  String name;
  int age;

  Animal(this.name, this.age);

  // 私有方法
  void _run() {
    print('私有方法');
  }

  // 公有方法
  execRun() {
    // 类里面方法的相互调用
    this._run();
  }
}
  • run()私有方法在Animal类的execRun()公有方法中,可通过访问公有方法间接地访问私有方法run()。
import './lib/AnimalMethods.dart';

void main() {
  Animal a = new Animal('小狗', 3);
  // 间接地调用私有方法
  a.execRun();
}

(3) getter与setter

class Rect {
  num height;
  num width;

  Rect(this.height, this.width);

  get area {
    return this.height * this.width;
  }

  set areaHeight(value) {
    this.height = value;
  }
}

void main() {
  Rect r = new Rect(10, 3);
  // 通过访问属性来访问area
  print('面积:${r.area}');

  r.areaHeight = 6;
  print('面积:${r.area}');
}

(4) 类的初始化列表

  • 可以在构造函数体运行之前初始化实例变量。
class Rect {
  int height;
  int width;

  // 初始化实例变量
  Rect(): height = 30, width = 10 {
    print('长为:${this.height}\n宽为:${this.width}');
  }

  getArea() {
    return this.height * this.width;
  }
}

void main() {
  Rect r = new Rect();
  print('面积:${r.getArea()}');
}

(5) 类中的静态成员

  • 使用static关键字来实现类级别的变量和函数。
  • 静态属性与静态方法直接通过类名访问,而非类的实例访问。
class Person {
  static String name = '张三';
  static void show() {
    print(name);
  }
}

main() {
  // var p = new Person();
  // p.show();
  print(Person.name);
  // 调用静态方法
  Person.show();
}
  • 非静态方法可以访问静态成员、非静态成员,以及静态方法。
  • 静态方法可以访问静态成员和静态方法,不能访问非静态成员和非静态方法。
class Person {
  static String name = '张三';
  int age = 21;

  static void show() {
    print(name);
  }

  void printInfo() {
    // 访问非静态属性
    print(this.age);
    // 访问静态属性
    print(name);
    // 调用静态方法
    show();
  }

  static void printUserInfo() {
    // 访问静态属性
    print(name);
    // 调用静态方法
    show();
    // 无法访问非静态属性
    // print(age);
    // print(this.age);
    // 无法访问非静态方法
    // printInfo();
    // this.printInfo();
  }
}

main() {
  Person p = new Person();
  p.printInfo();

  Person.printUserInfo();
}

6-4 类的继承

  • 子类使用extends关键词来继承父类,子类能复写父类的方法。
  • 子类会继承父类里面可见的属性和方法,但不会继承构造函数。
class Person {
  String name = '张三';
  num age = 21;
  void printInfo() {
    print('${this.name} --- ${this.age}');
  }
}

// 可以访问Person中共有的属性和方法
class Web extends Person {}

main() {
  // 实例化Web类
  Web w = new Web();
  print(w.name);
  w.printInfo();
}

(1) super关键词

class Person {
  String name;
  num age;
  Person(this.name, this.age);
  void printInfo() {
    print('${this.name} --- ${this.age}');
  }
}

class Web extends Person {
  // super表示实例化子类时,将传过来的name和age参数赋值给父类Person
  Web(String name, num age): super(name, age) {}
}

main() {
  Web w = new Web('张三', 21);
  w.printInfo();
}

(2) 子类扩展属性和方法

class Person {
  String name;
  num age;
  Person(this.name, this.age);
  void printInfo() {
    print('${this.name} --- ${this.age}');
  }
}

class Web extends Person {
  // 定义子类自己的属性
  String sex;
  Web(String name, num age, String sex): super(name, age) {
    this.sex = sex;
  }
  // 定义子类自己的方法
  run() {
    print('${this.name} --- ${this.age} --- ${this.sex}');
  }
}

main() {
  Web w = new Web('张三', 21, '男');
  w.printInfo();
  w.run();
}

(3) 给命名构造函数传参

class Person {
  String name;
  num age;
  Person(this.name, this.age);
  // 命名构造函数
  Person.construct(this.name, this.age);
  void printInfo() {
    print('${this.name} --- ${this.age}');
  }
}

class Web extends Person {
  String sex;
  Web(String name, num age, String sex): super.construct(name, age) {
    this.sex = sex;
  }
  run() {
    print('${this.name} --- ${this.age} --- ${this.sex}');
  }
}

main() {
  Web w = new Web('张三', 21, '男');
  w.printInfo();
  w.run();
}

(4) 子类覆写父类的方法

class Person {
  String name;
  num age;
  Person(this.name, this.age);
  void printInfo() {
    print('${this.name} --- ${this.age}');
  }

  work() {
    print('${this.name}在工作');
  }
}

class Web extends Person {
  Web(String name, num age): super(name, age);
  // 子类中扩展方法
  run() {
    print('run');
  }

  // 覆写父类的方法,不加@override也可以,建议加上
  
  void printInfo() {
    print('姓名:${this.name} --- 年龄:${this.age}');
  }

  
  work() {
    print('${this.name}的日常工作任务是写代码');
  }
}

main() {
  // 实例化子类
  Web w = new Web('张三', 21);
  w.printInfo();
  w.work();
}

(5) 子类中调用父类方法

class Person {
  String name;
  num age;
  Person(this.name, this.age);
  void printInfo() {
    print('${this.name} --- ${this.age}');
  }
  
  work() {
    print('${this.name}在工作');
  }
}

class Web extends Person {
  Web(String name, num age): super(name, age);
  // 子类中扩展方法
  run() {
    print('程序执行中');
    // 子类调用父类方法
    super.work();
  }

  
  void printInfo() {
    print('姓名:${this.name} --- 年龄:${this.age}');
  }
}

main() {
  // 实例化子类
  Web w = new Web('张三', 21);
  w.printInfo();
  w.run();
}

6-5 抽象类与泛型

  • 抽象类主要通过abstract关键词用于定义标准,子类可继承抽象类,也可实现抽象类接口。
    • 抽象类不能被实例化,只有继承它的子类可以。
    • 如果子类继承抽象类,必须实现它的抽象方法。
    • 没有方法体的方法称之为抽象方法,不能用abstract关键词来声明。
    • 若把抽象类当接口实现,须先实现抽象类中定义的所有属性和方法。
  • extends抽象类和implements的区别
    • 如果只是把抽象类当做标准,那么就用implements关键词来实现抽象类。
    • 若要复用抽象类中的方法且用抽象方法约束子类,用extends继承抽象类。
// 定义一个Animal类,要求它的子类必须包含eat方法
abstract class Animal {
  // 抽象方法
  eat();
  // 非抽象方法
  printInfo() {
    print('抽象类中的普通方法');
  }
}

class Dog extends Animal {
  
  eat() {
    print('小狗在吃美味的骨头');
  }
}

main() {
  Dog d = new Dog();
  d.eat();
  d.printInfo();

  // 抽象类没法直接被实例化,只有继承它的子类才能被实例化
  // var a = new Animal();
}

(1) 多态

  • 多态就是父类定义一个方法不去实现,让继承它的子类去实现,每个子类有不同的表现。
abstract class Animal {
  // 抽象方法
  eat();
}

class Dog extends Animal {
  
  eat() {
    print('小狗在吃美味的骨头');
  }
}

class Cat extends Animal {
  
  eat() {
    print('小猫在吃可爱的老鼠');
  }
}

main() {
  Dog d = new Dog();
  d.eat();
  Cat c = new Cat();
  c.eat();
}
  • 允许将子类类型的指针赋值给父类类型的指针,同一个函数调用会有不同的执行效果。
abstract class Animal {
  // 抽象方法
  eat();
}

class Dog extends Animal {
  
  eat() {
    print('小狗在吃美味的骨头');
  }

  run() {
    print('程序正在执行过程中');
  }
}

class Cat extends Animal {
  
  eat() {
    print('小猫在吃可爱的老鼠');
  }

  run() {
    print('程序正在执行过程中');
  }
}

main() {
  // 子类的实例赋值给父类的引用
  Animal d = new Dog();
  d.eat();
  Animal c = new Cat();
  c.eat();
}

(2) 接口

  • Dart的接口没用interface关键字定义,普通类或抽象类都可作为接口,使用implements关键字实现。
    • 如果实现接口的类是普通类,那么Dart会将普通类和抽象类里面属性的方法全部覆写一遍。
    • 抽象类可定义抽象方法,普通类不可,要实现类似于Java接口的方式,一般都会用抽象类。
  • 建议使用抽象类定义接口。
// 接口,即约定或叫规范,定义一个DB库,支持MySQL
abstract class DB {
  // 数据库的链接地址
  String uri;
  add(String data);
  edit();
  delete();
}

class MySQL implements DB {
  
  String uri;
  MySQL(this.uri);

  
  add(String data) {
    print('这是MySQL的add方法:$data');
  }

  
  edit() {
    return null;
  }

  
  delete() {
    return null;
  }
}

main() {
  MySQL mysql = new MySQL('127.0.0.1');
  mysql.add('123456789');
}
  • 抽类步骤一:lib目录新建一个DB.dart文件。
// 接口,即约定或叫规范,DB.dart文件
abstract class DB {
  // 数据库的链接地址
  String uri;
  add(String data);
  edit();
  delete();
}
  • 抽类步骤二:lib目录新建一个MySQL.dart文件,引入DB.dart文件。
import 'DB.dart';

// MySQL.dart文件
class MySQL implements DB {
  
  String uri;
  MySQL(this.uri);

  
  add(String data) {
    print('这是MySQL的add方法:$data');
  }

  
  edit() {
    return null;
  }

  
  delete() {
    return null;
  }
}
  • 抽类步骤三:创建一个Test.dart文件,用于引入MySQL.dart文件。
import 'lib/MySQL.dart';

// Test.dart文件
main() {
  MySQL mysql = new MySQL('127.0.0.1');
  mysql.add('123456789');
}

(3) 多接口

// 一个类实现多个接口
abstract class A {
  String name;
  printA();
}

abstract class B {
  printB();
}

class C implements A, B {
  
  String name;

  
  printA() {
    print('打印A');
  }

  
  printB() {
    print('打印B');
  }
}

void main() {
  C c = new C();
  c.printA();
  c.printB();
}

(4) mixins

  • mixins即混入,指在类中混入其他功能,在Dart中可以使用mixins实现类似多继承的功能。
  • 使用的条件伴随着Dart版本的变化一直在改变,这里用的是Dart 2.x中使用mixins的条件。
  • 一个类可以mixins多个mixins类,mixins绝不是继承,也不是接口,而是一种全新的特性。
class A {
  String info = '这是A';
  void printA() {
    print('A');
  }
}

class B {
  void printB() {
    print('B');
  }
}

class C with A, B {}

void main() {
  var c = new C();
  c.printA();
  c.printB();
  print(c.info);
}
  • 作为mixins的类只能继承自Object,不能继承其他类。
class Person {
  printInfo() {
    print('Person类');
  }
}

class A extends Person {
  String info = '这是A';
  void printA() {
    print('A');
  }
}

class B {
  void printB() {
    print('B');
  }
}

// 这里A类继承了Person类,C类mixinsA类时会报错
// class C with A, B {}

void main() {}
  • 作为mixins的类不能有构造函数。
class A {
  String info = '这是A';
  void printA() {
    print('A');
  }
}

class B {
  // 写构造函数会报错
  // B() {};
  void printB() {
    print('B');
  }
}

class C with A, B {}

void main() {
  var c = new C();
  c.printA();
  c.printB();
  print(c.info);
}
  • 继承与mixins一起写的方法。
class Person {
  String name;
  Object age;
  Person(this.name, this.age);
  printInfo() {
    print('${this.name} --- ${this.age}');
  }
}

class A {
  String info = '这是A';
  void printA() {
    print('A');
  }
}

class B {
  void printB() {
    print('B');
  }
}

// C类具有Person类、A类和B类的功能
// class C with Person, A, B {}
// 此时Person类中可以有构造函数
class C extends Person with A, B {
  C(String name, Object age): super(name, age);
}

void main() {
  var c = new C('张三', 21);
  c.printInfo();
}
  • 注意with A, Bwith B, A的顺序,打印出来的内容不一样,后面替换前面。
class Person {
  String name;
  Object age;
  Person(this.name, this.age);
  printInfo() {
    print('${this.name} --- ${this.age}');
  }

  // 继承与mixins中有同样的方法,with A, B或with B, A的顺序同下
  void run() {
    print('Person类执行中');
  }
}

class A {
  String info = '这是A';
  void printA() {
    print('A');
  }

  void run() {
    print('A执行中');
  }
}

class B {
  void printB() {
    print('B');
  }

  void run() {
    print('B执行中');
  }
}

// 注意with A, B或with B, A的顺序,打印内容不一样,后面替换前面
class C extends Person with A, B {
  C(String name, Object age): super(name, age);
}

void main() {
  var c = new C('张三', 21);
  c.run();
}
  • mixins的实例类型就是其超类的子类型。
// ignore_for_file: unnecessary_type_check

class A {
  String info = '这是A';
  void printA() {
    print('A');
  }
}

class B {
  void printB() {
    print('B');
  }
}

class C with A, B {}

void main() {
  var c = new C();
  print(c is A);
  print(c is B);
  print(c is C);

  var a = new A();
  print(a is Object);
}

(5) 泛型方法

  • 泛型即解决类、接口、方法的复用性,以及对不特定数据类型的支持(类型校验)。
// 只能返回String类型的数据
String getData1(String value) {
  return value;
}

// 同时支持返回String类型和int类型,代码冗余
String getData2(String value) {
  return value;
}

int getData3(int value) {
  return value;
}

// 同时返回String类型和number类型,不指定类型可以解决问题
getData4(value) {
  return value;
}

// 不指定类型放弃了类型检查,需要实现的是传入什么就返回什么
T getData5<T>(T value) {
  return value;
}

void main() {
  print(getData5(21));
  print(getData5('张三'));
  
  getData5<int>(22);
  getData5<String>('李四');
  getData5<int>(23);
  getData5<String>('王五');
}

(6) 泛型类定义

class MyList1 {
  List list = <int>[];
  void add(int value) {
    this.list.add(value);
  }

  List getList() {
    return list;
  }
}

// 定义泛型类
class MyList2<T> {
  List list = <T>[];
  void add(T value) {
    this.list.add(value);
  }

  List getList() {
    return list;
  }
}

main() {
  // 创建固定长度的List,这里使用到了泛型
  List l1 = new List<String>.filled(2, '');
  l1[0] = '121';
  l1[1] = '张三';
  print(l1);

  // new关键词可以省略
  MyList1 l2 = MyList1();
  l2.add(123);
  l2.add(321);
  print(l2.getList());

  MyList2 l3 = MyList2();
  l3.add(125);
  l3.add('李四');
  print(l3.getList());

  MyList2 l4 = MyList2<String>();
  l4.add('王五');
  // 传int会报错
  l4.add('135');
  print(l4.getList());
}

(7) 泛型接口定义

  • 实现数据缓存的功能:文件缓存和内存缓存,这两种功能按接口约束实现。
  • 定义一个泛型接口
    • 约束实现子类必须有getByKey(Key)setByKey(key, value)
    • 要求setByKey的value类型和实例化子类时指定的类型需保持一致。
// 定义一个接口
abstract class Cache<T> {
  // 定义两个抽象方法
  getByKey(String key);
  void setByKey(String key, T value);
}

// 定义两个类FileCache和MemoryCache
class FileCache<T> implements Cache<T> {
  
  getByKey(String key) {
    return null;
  }

  
  void setByKey(String key, T value) {
    print('我是文件缓存,把key=${key}、value=${value}的数据写入到了文件中');
  }
}

class MemoryCache<T> implements Cache<T> {
  
  getByKey(String key) {
    return null;
  }

  
  void setByKey(String key, T value) {
    print('我是内存缓存,把key=${key}、value=${value}的数据写入到了内存中');
  }
}

void main() {
  MemoryCache m1 = new MemoryCache<String>();
  m1.setByKey('index', '首页数据');
  MemoryCache m2 = new MemoryCache<Map>();
  m2.setByKey('index', {'姓名': '张三', '工作': '码农'});
}

文章作者: bsf
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 bsf !
评论
 上一篇
Flutter介绍 Flutter介绍
Flutter横跨Android、iOS、MacOS、Windows、Linux等多个系统,还可以打包成Web程序运行在浏览器上。
2023-03-05
本篇 
Dart基础 Dart基础
Dart诞生于2011年10月10日,是一种“结构化的Web编程”语言,应用于Web、服务器、移动应用等领域的开发。
2023-01-08
  目录