[Dart] 入门学习

Dart

基于B站视频 Dart 学习

基础

注释

// 单行注释
/* 多行注释 */
/// 文档注释(支持markdown)

变量

万物皆对象,变量名称是引用

声明

  • 明确指定类型:int age = 18
  • 不明确类型:var age = 18dynamic age = 18

注意:

  • 变量默认值是 null
  • 变量不会隐式转换

常量

  • const age = 18
  • final age = 18
const time = DateTime.now();  // 报错,不可以将运行时的值分配给 const 常量
final time = DateTime.now();  // 正确,可以将运行时的值分配给 final 变量

数据类型

Number, String, Boolean, List, Set, Map, ...

Number

  • num 整数/小数
  • int 整数
  • double 浮点数

常用API:NUM, INT, DOUBLE

int count = 3;
// int count = 3.7  错
print(count);

double price = 3;
print(price); // 3.0
price = 3.7;
print(price); // 3.7

num n1 = 3; // 自动确定数据类型
print(n1); // 3
n1 = 3.7;
print(n1); // 3.7

print(n1.toInt()); // 3
print(3.8.toInt()); // 3
print(3.14.round()); // 3 四舍五入至整数将
print(3.14.toStringAsFixed(1)); // 3.1 四舍五入至小数第1位
print(10.remainder(4)); // 2 取余
print(10.compareTo(12)); // -1 比较(-1 小于; 0 等于; 1 大于)
print(12.gcd(18)); // 6 最大公约数
print(1000.toStringAsExponential(2)); // 科学记数法,保留2位小数

String

String

  • 单引号、双引号均可
  • 三引号可包含换行符

RegExp(r'正则表达式') 正则表达式
eg. RegExp(r'\d+')

var str1 = 'Hello World';
print(str1);

String str2 = "RUA";
print(str2);

String str3 = """
Hello
""";
print(str3);

print(str1 + str2); // Hello WorldRUA 字符串拼接
print("$str1 $str2"); // Hello World RUA 模板字符串

print(str1.split(" ")); // [Hello, World] 分割字符串 -> List
print("   abc     ".trim()); // abc 去空格

print("".isEmpty); // true
print("".isNotEmpty); // false

print(str1.replaceAll("World", "Java")); // Hello Java 字符串替换
print('ue21j3oi109irw'.replaceAll(RegExp(r"\d+"), '_')); // ue_j_oi_irw 支持正则

var isPhone = RegExp(r"^1\d{10}$");
print(isPhone.hasMatch('12345678901')); // true
print(isPhone.hasMatch('1234567801')); // false

print(str1.contains("He")); // true
print(str1.indexOf("o")); // 4
print(str1.lastIndexOf("o")); // 7

Boolean

bool
值:true, false

要显式检查布尔值,如 if(var) 不合法; if(var == null) 合法

List

List

字面量方式

  • 不限定元素的数据类型: List list = [];
  • 限定元素的数据类型: List list = <int>[];
List l1 = ['a', 'b', 'c']; //[a, b, c]
print(l1);

List l2 = [
  1,
  'a',
  [2, 3]
  ];
print(l2); // [1, a, [2, 3]]

List l3 = <int>[1, 2, 3];
print(l3); // [1, 2, 3]

构造函数方式

  • 不限定元素长度的空列表: List list = new List.empty(growable: true);
  • 指定长度的填充列表: List list = new List.fill(3, 0);
// 通过构造函数的声明方式
// var l3 = new List();  // 已弃用
var l4 = new List.empty(); // 默认 growable = False
print(l4); // []

//l4.add(1);  // 不能变更大小

var l5 = new List.empty(growable: true);
l5.add(1);
print(l5); // [1]

var l6 = new List.filled(3, 6);
print(l6); // [6, 6, 6]

扩展操作符

  • var list [1, 2, 3];
  • var list1 = [0, ...list]; // [0, 1, 2, 3]
var l7 = [0, ...l6];
print(l7); // [0, 6, 6, 6]

var l8;
// var l9 = [7, ...l8];  // 报错,l8 是 Null, 不能扩展

var l10 = [7, ...?l8];
print(l10); // [7]

// 列表长度
print(l1.length); // 3

相关操作

// 列表反转
print(l1.reversed); // (c, b, a) 可迭代内容
print(l1.reversed.toList()); //[c, b, a]

// 添加元素
l3.addAll(<int>[4, 5, 6]);
print(l3); // [1, 2, 3, 4, 5, 6]

// 删除元素
l3.remove(6);
print(l3); // [1, 2, 3, 4, 5]

// 删除元素(下标)
l3.removeAt(1);
print(l3); // [1, 3, 4, 5]

// 添加元素
l3.insert(1, 9);
print(l3); // [1, 9, 3, 4, 5]

l3.clear();
print(l3); // []
print(l3.length); // 0
print(l3.isEmpty); // true

// 合并元素
List words = ['Hello', 'World'];
print(words.join('-')); // Hello-World  与 split 相反

遍历

  • 遍历列表 forEach()
  • 遍历并处理元素,然后生成新的列表 map()
  • 返回满足条件的数据 where()
  • 只要有一项满足条件 any()
  • 所有项全部满足条件 every()
var nums = [1, 2, 3];

// for
for (var i = 0; i < nums.length; i++) {
print(nums[i]);
} // 1 2 3

// for in
for (var item in nums) {
print(item);
} // 1 2 3

// forEach
nums.forEach((element) {
print(element);
}); // 1 2 3

// map 的近似原理
var newNums = [];
for (var i = 0; i < nums.length; i++) {
newNums.add(nums[i] * nums[i]);
}
print(newNums); // [1, 4, 9]

// map
var newNums2 = nums.map((e) {
return e * e;
});
print(newNums2); // (1, 4, 9)
print(newNums2.toList()); // [1, 4, 9]

// where() 返回符合条件的元素
// eg. 判断是否为奇数
bool isOdd(n) => n % 2 == 1; // 判断用的函数
var oddNum = nums.where((element) => isOdd(element));
print(oddNum); // (1, 3)

// any()
print(nums.any(isOdd)); // true
print(nums.any((n) => n % 2 == 1)); // true

// every()
print(nums.every(isOdd)); // false

// 扩展
var pairs = [
[1, 2],
[3, 4]
];
var flattened = pairs.expand((element) => element);
print(flattened); // (1, 2, 3, 4)

// 折叠 - 对列表中的每一个元素,做一个累计操作
int result = nums.fold(2, (p, element) => p * element); // 2 * (1*2*3)
int result2 = nums.fold(2, (p, element) => p + element); // 2 + (1*2*3)
print(result); // 12
print(result2); // 8

set

无序唯一

  • 字面量声明
  • 构造函数声明

不能用 [] 访问,但有 .first.last

// 声明:字面量
var nums = <int>{1, 2, 3};
print(nums); // {1, 2, 3}

var nums1 = <int>{1, 2, 3, 3}; // 有个小蓝线
print(nums1); // {1, 2, 3}

var fruits = new Set();
fruits.add("Warma");
fruits.add("Wdnmd");
print(fruits); // {Warma, Wdnmd}
print(fruits.toList()); // [Warma, Wdnmd]

List nums2 = [1, 2, 3, 3, 4];
print(nums2.toSet()); // {1, 2, 3, 4} 去重

// 集合特有的操作
var caocao = new Set();
caocao.addAll(['张辽', '司马懿', '关羽']);
var liubei = new Set();
liubei.addAll(['关羽', '张飞', '诸葛亮']);

// 求交集
print(caocao.intersection(liubei)); // {关羽}

// 求并集
print(caocao.union(liubei)); // {张辽, 司马懿, 关羽, 张飞, 诸葛亮}

// 求差集
print(caocao.difference(liubei)); // {张辽, 司马懿}

// 返回第一个
print(caocao.first); // 张辽

// 返回最后一个
print(caocao.last); // 关羽

Map

无序键值对

  • var map = {key1: val1, key2: val2};
  • var map = new Map();
    • map['key'] = val;
// 声明 字面量
var person = {'name': '张三', 'age': 20};
print(person); // {name: 张三, age: 20}

// 声明 构造函数
var p = new Map(); // new 可以省略
p['name'] = '李四';
p['age'] = 22;
print(p); // {name: 李四}

// 访问
print(p['name']); // 李四

// 判断 Map 中的 key 是否存在
print(p.containsKey('name')); // true
print(p.containsValue(22)); // trye

// 赋值(如果key已经存在,无法赋值)
p.putIfAbsent('gender', () => '武装直升机');
print(p); // {name: 李四, age: 22, gender: 武装直升机}

p.putIfAbsent('gender', () => '武直');
print(p); // {name: 李四, age: 22, gender: 武装直升机}

// 获取 Map 中所有 key
print(p.keys); // (name, age, gender)

// 获取 Map 中所有 value
print(p.values); // (李四, 22, 武装直升机)

// 根据条件删除
p.removeWhere((key, value) => key == 'gender');
print(p); // {name: 李四, age: 22}

其他

Runes(符文)

32位字符对象(utf-32),它可以把文字转换成符号表情或特定文字
Char

var str = '😂'; // 😂 可能不太一样
print(str);
print(str.length); // 2 dart 中中字符串是 utf-16, 上面是 utf-8
print(str.runes.length); // 1

// Runes
Runes input = new Runes('\u{1f680}'); // \u 后面超过4位要写大括号
print(new String.fromCharCodes(input)); // 🚀

Symbol

# 开头来表示的标识符

// Symbol
var a = #abc;
print(a); // Symbol("abc")

var b = new Symbol("abc");
print(b); // Symbol("abc")

print(#abc == new Symbol('abc')); // true

dynamic

动态数据类型

// dynamic
dynamic foo = "bar";
print(foo);
foo = 123;
print(foo);

运算符

地板除 ~/

// 地板除
print(7 ~/ 4); // 1

// 类型判断运算符
List list = [];
if (list is List) {
    print('list is List'); // ✔
} else {
    print("list is not List");
}

if (list is! List) {
    print('list is not List');
} else {
    print("list is List"); // ✔
}

类型判断运算符 isis!

// 避空运算符
print(1 ?? 3); // 1 不是 null,所以返回 1
print(null ?? 12); // 12

var foo;
print(foo ?? 233); // 233
foo = 0;
print(foo ?? 233); // 0

避空运算符 ????=

// 避空运算符
var a;
if (a == null) {
    a = 3;
} // 相当于:
a ??= 3;
print(a); // 3

a ??= 4; // 不成功
print(a); // 3

条件属性访问 ?.

存在属性再访问

// 条件属性运算符
var m = new Map();
print(m.length); // 0 可以正常访问

var obj;
print(obj?.length); // null

级联运算符 ..

  • myObj.myMethod(); -> myMethod() 的返回值
  • myObj..myMethod(); -> myObj 对象的引用
// 级联运算符
Set s = new Set();
s.add(1);
s.add(2);
s.add(3);
s.remove(2);
print(s); // {1, 3}

Set ss = new Set();
ss
..add('a')
..add('b')
..add('c')
..remove('b');

print(ss); // {a, c}

函数

声明函数

直接声明

无关键字

void printInfo() {
  print("Hello, QAQ");
}

int getNum() {
  return 1; // 返回至与声明类型要一致
}

void main(List<String> args) {
  printInfo(); // Hello, QAQ
  print(getNum()); // 1
}

箭头函数

只能有一行,不能带有结束分号,
仅是一种函数的简写

// 箭头函数
fruits.forEach((element) => {print(element)}); // 箭头函数中没有封号
fruits.forEach((element) => print(element)); // 同上,也可以省略{}

匿名函数

// 匿名函数
var myPrint = (value) {
    print(value);
};

List fruits = ['苹果', '栗子'];
fruits.forEach((element) {
    print(element);
}); // 等效于
fruits.forEach(myPrint); // 苹果 \n  栗子

立即执行函数

// 立即执行函数
((int n) {
print(n); // 17
})(17);

函数参数

必填参数

  • 参数类型 参数名称
// 必填参数
String userInfo(String name) {
    return "你好哇, $name";
}

String res = userInfo("张三");
print(res); // 你好哇, 张三

可选参数

  • 必须在必选参数后面
  • 通过 [] 括起来
  • 可带默认值
// 可选参数
String userInfo2(String name, [int age = 233]) {
    return "你好哇, $name, 年龄: $age";
}

String res2 = userInfo2("李四");
print(res2); // 你好哇, 李四, 年龄: 233

String res3 = userInfo2("李四", 666);
print(res3); // 你好哇, 李四, 年龄: 666

命名参数

  • {} 括起来
  • 调用函数时,命名参数的名称与声明函数中的名称保持一致
// 命名参数
String userInfo3(String name, {int age = 233}) {
    return "你好哇, $name, 年龄: $age";
}

String res4 = userInfo3("李四", age: 666); // 命名参数调用时,需要与声明参数一致
print(res2); // 你好哇, 李四, 年龄: 233

函数参数

// 函数参数
var myPrint = (val) => print(val);
List fruits = ["汽车", "火箭"];
fruits.forEach(myPrint);  // 汽车 \n 火箭

作用域和闭包

  • 闭包
    • 使用时机:既能重用变量,又能保护变量不被污染
var globalNum = 100; // 全局变量

void main(List<String> args) {
  printInfo() {
    var localNum = 10; // 局部变量
    print(localNum); // 10
    print(globalNum); // 100, 内层可以访问外层
  }

  printInfo();
  // print(localNum); // 报错, 外层不可以访问内层

  parent() {
    var money = 1000;
    return () {
      money -= 100;
      print(money);
    };
  }

  var p = parent();
  p(); // 900
  p(); // 800
  p(); // 700
}

异步函数

async 返回一个 Future, await 用于等待 Future

通过 then

import 'package:http/http.dart' as http;
import 'dart:convert';

Future getIPAddress() {
  final url = 'https://httpbin.org/ip';
  return http.get(Uri.parse(url)).then((response) {
    // print(response.body);
    String ip = jsonDecode(response.body)['origin'];
    return ip;
  });
}

void main(List<String> args) {
  getIPAddress().then((ip) => print(ip)).catchError((error) => print(error));
}

通过 async

import 'package:http/http.dart' as http;
import 'dart:convert';

Future getIPAddress() async {
  final url = 'https://httpbin.org/ip';
  final response = await http.get(Uri.parse(url));
  String ip = jsonDecode(response.body)['origin'];
  return ip;
}

void main(List<String> args) async {
  try {
    final ip = await getIPAddress();
    print(ip);
  } catch (error) {
    print(error);
  }
}

类与对象

简介

类 通过 class 声明,包括

  • 属性:用来描述类的变量
  • 方法:类中的函数称为类的方法

对象是类的实例化结果

class Person {
  // 类的属性
  String name = "张三";

  // 类的方法
  void getInfo() {
    print("我是$name");
  }
}

void main(List<String> args) {
  // 实例化类,得到一个对象
  Person p = new Person();

  // 访问类中的属性
  print(p.name); // 张三

  // 访问类的方法
  p.getInfo(); // 我是张三

  // dart 万物皆为对象
  Map m = new Map();
  print(m.length); // 0

  m.addAll({'name': 'Rika', 'age': 100});
  print(m.length); // 2
}

构造器(构造函数)

默认构造函数

与类名同名,在实例化时自动被调用

class Point {
  num x, y;

  // 普通构造函数
  Point() {
    print("默认构造函数,实例化时被自动调用");
    x = 10; // this 可以省略,但当 x 有歧义时,要加 this
    // y = 10;
    // this.x = 20;
    this.y = 20;
  }
}

class Point2 {
  num x, y;
  Point2(num x, num y) {
    this.x = x;
    this.y = y;
  }
}

class Point3 {
  num x, y;

  Point3(this.x, this.y);
}

void main(List<String> args) {
  var p = new Point();
  print("${p.x}, ${p.y}"); // 10, 20

  var p2 = new Point2(3, 4);
  print("${p2.x}, ${p2.y}"); // 3, 4

  var p3 = new Point3(6, 7);
  print("${p3.x}, ${p3.y}");  // 6, 7
}

命名构造函数

在类中使用命名构造器(类名.函数名)实现多个构造器,可以提供额外清晰度

class Point {
  num x, y;

  Point(this.x, this.y);

  Point.origin() {
    x = 0;
    y = 0;
  }

  Point.fromJson({x: 0, y: 0}) {
    this.x = x;
    this.y = y;
  }
}

void main(List<String> args) {
  Point p1 = new Point.origin();
  print(p1.x); // 0

  Point p2 = new Point.fromJson(x: 6, y: 6);
  print(p2.x); // 6
}

常量构造函数

如果类生成的对象不会改变,可以通过常量构造函数使这些对象成为编译时常量
要点:

  • 所有属性必须通过 final 声明
  • 常量构造函数必须通过 const 声明
  • 不能有函数体
class Point {
  num x, y;

  Point(this.x, this.y);
}

class ImmutablePoint {
  // 属性必须通过 final 声明
  final num x, y;

  // 常量构造函数必须通过 const 声明,且不能有函数体
  const ImmutablePoint(this.x, this.y);
}

void main(List<String> args) {
  // new 可以省
  var p1 = Point(1, 2);
  var p2 = Point(1, 2);
  print(p1 == p2); // false

  // 常量构造函数可以当作普通构造函数使用
  var p3 = new ImmutablePoint(3, 4);
  var p4 = new ImmutablePoint(3, 4);
  print(p3 == p4); // false

  // 声明不可变对象必须通过 const 关键字
  var p5 = const ImmutablePoint(3, 4);
  var p6 = const ImmutablePoint(3, 4);
  print(p5 == p6); // true
}

工厂构造函数

通过 factory 声明,工厂构造函数不会自动生成实例,而是通过代码来决定返回实例

class Person {
  String name;

  static Person instance;

  factory Person([String name = "Rika"]) {
    // 工厂构造函数内,不能使用 this 关键字
    // print(this.name);

    if (Person.instance == null) {
      // 第一次实例化
      Person.instance = new Person.newSelf(name);
    }
    // 非第一次实例化
    return Person.instance;
  }

  // 命名构造函数
  Person.newSelf(this.name);
}

void main(List<String> args) {
  var p1 = new Person();
  print(p1.name); // Rika

  var p2 = new Person("Reina");
  print(p2.name); // Rika

  print(p1 == p2); // true
}

访问修饰

  • 没有访问修饰符
  • 默认是公开的
  • 如果属性或方法以 _ 开头,即表示私有(库内可见)
  • 如果与 mian 函数在同一作用域,私有属性或方法不起作用
  • 只有把类单独抽离出去,私有属性或方法才起作用(扔另一个文件中)

错误方法:写在同一文件中

class Person {
  String name;

  // 声明私有属性
  num _money = 666;

  Person(this.name);
}

void main(List<String> args) {
  var p = new Person("Reina");
  print(p.name);
  print(p._money); // 666
}

正确方法:

./lib/Person.dart

class Person {
  String name;

  // 声明私有属性
  num _money = 666;

  Person(this.name);

  num getMoney() {
    return this._money;
  }

  void _friend() {
    print("Satoko");
  }
}

./private.dart

import 'lib/Person.dart';

void main(List<String> args) {
  var p = new Person("Rika");
  print(p.name);
  // print(p._money); 无法访问
  print(p.getMoney()); // 666

  // p._friend(); 无法访问
}

Getter 和 Setter

  • Getter (获取器)通过 get 修饰的方法
    • 函数没有小括号,访问时也没有小括号(像访问属性一样访问方法)
  • Setter (修改器)通过 set 关键字修饰的方法
    • 访问时,像设置属性一样给函数传参
class Circle {
  final double PI = 3.1415926;
  num r;

  Circle(this.r);

  num area() {
    return this.PI * this.r * this.r;
  }

  // 使用 get 声明的方法,不能有小括号
  num get area2 {
    return this.PI * this.r * this.r;
  }

  set setR(value) {
    this.r = value;
  }
}

void main(List<String> args) {
  var c = new Circle(10);
  print(c.area()); // 314.15926
  print(c.area2); // 314.15926

  c.setR = 20;
  print(c.area2); // 1256.63704
}

初始化列表

  • 作用:在构造函数中设置属性的默认值
  • 时机:在构造函数执行前之前
  • 语法:用 , 分割初始化表达式
  • 场景:设置 final 的默认值
class Rect {
  int height;
  int width;

  // Rect(this.height, this.width);

  Rect([int height = 2, int width = 10]) {
    this.height = height;
    this.width = width;

    print("${this.height}, ${this.width}");
  }
}

class Rect2 {
  int height, width;
  Rect2({int height = 2, int width = 10}) {
    this.height = height;
    this.width = width;

    print("${this.height}, ${this.width}");
  }
}

// 初始化列表
class Rect3 {
  int height, width;

  Rect3()
      : height = 4,
        width = 20 {
    print("${this.height}, ${this.width}");
  }
}

class Point {
  double x, y, z;

  Point(this.x, this.y, this.z);

  // 初始化列表的特殊用法(重定向构造函数)
  Point.twoD(double x, double y) : this(x, y, 0);
}

void main(List<String> args) {
  var r = new Rect(); // 2, 10
  var r2 = new Rect2(); // 2, 10
  var r3 = new Rect3(); // 4, 20

  var p1 = new Point(1, 2, 3);
  print(p1.z); // 3.0

  var p2 = new Point.twoD(2, 3);
  print(p2.z); // 0.0
}

static

  • static 用来指定静态成员
  • 静态成员可以通过类名直接访问,不需要实例化(可节省资源)

注意:

  • 静态方法不能访问静态成员
  • 静态方法可以访问静态成员
  • 静态方法中不能使用 this 关键字
  • 不能使用 this 关键字访问静态属性
class Person {
  static String name = "Reina";
  int age = 18;

  static printInfo() {
    // print(this.name); // 不能通过 this 访问静态属性
    print(name);

    // print(age); //  静态方法不能访问非静态属性
    // printUserInfo();  // 静态方法不能访问非静态方法
  }

  printUserInfo() {
    print(name); // 非静态方法可以访问静态成员
    print(age);

    printInfo(); // 非静态方法可以访问静态方法
  }
}

void main(List<String> args) {
  // 静态成员可以通过类名直接访问
  print(Person.name); // Reina
  Person.printInfo(); // Reina

  // print(Person.printUserInfo()); // 不能使用类名访问非静态方法

  var p = new Person();
  // print(p.name);  // 不能通过实例化对象访问静态属性
  p.printUserInfo(); // Reina \n 18 \n Reina
}

元数据

元数据以 @ 开头,可以给代码标记一些额外的信息

元数据可以用在 库、类、构造器、函数、字段、参数、变量声明的前面

  • @override (重写):可用来注解某方法,表重写父类中的同名方法
  • @required (必填):可用来注解命名参数,指示它为必填参数
  • @deprecated (弃用):可用来注解某类或某方法,表此方法或类不再建议使用
class Phone {
  @deprecated
  activate() {
    // 这是旧版本的东东
    turnOn();
  }

  turnOn() {
    print("开机");
  }
}

void main(List<String> args) {
  var p = new Phone();
  p.activate(); // 开机
  p.turnOn(); // 开机
}

继承

子类通过 extends 继承父类

继承后,子类可使用父类中的可见的内容(属性或方法)(可见:公有)

  • 子类中,可以通过 @override 元数据来标记“覆写”方法
    • “覆写”方法:子类中与父类同名方法
  • 子类中,可以通过 super 关键字来引用父类中可见的内容(属性、方法(普通构造函数、命名构造函数))

基础内容

class Father {
  String name = "刘备";
  num money = 10000;

  say() {
    print("我是 $name");
  }
}

class Son extends Father {
  @override
  say() {
    print("我是 刘禅");
  }
}

void main(List<String> args) {
  var f = new Father();
  print(f.name); // 刘备

  var s = new Son();
  print(s.name); // 刘备
  print(s.money); // 10000
}

涉及私有属性:

./lib/Father.dart

class Father {
  String name = "刘备";
  num _money = 10000;
  String job;

  Father(this.job);
  Father.origin(this.job);

  say() {
    print("我是 $name");
  }

  get getMoney {
    return this._money;
  }
}

./lib/Son.dart

import 'Father.dart';

class Son extends Father {
  String name = "刘禅";

  // 通过 super 继承父类的普通构造函数
  Son(String job) : super(job);

  // 继承命名构造函数
  // Son(String job) : super.origin(job); // 对的,普通构造函数去继承命名构造函数
  Son.origin(String job) : super.origin(job);

  @override
  say() {
    super.say(); // super 指父类,调用父类的 .say(),name 属性是自己的
    print("我是 $name, 我爹是 ${super.name}, 工作是${super.job}");
  }
}

./main.dart

import 'lib/Father.dart';
import 'lib/Son.dart';

void main(List<String> args) {
  var f = new Father('皇帝');
  print(f.name); // 刘备

  var s = new Son.origin("卖草鞋的");
  print(s.name); // 刘备
  // print(s.money); // 10000 私有类,无法继承

  print(s.getMoney);
  s.say(); // 我是刘禅 \n 我是 刘禅, 我爹是 刘备, 工作是卖草鞋的
}

抽象类

  • 声明:abstract 修饰的类
  • 作用:充当普通类的模板,约定一些必要的属性和方法
  • 抽象方法是指没有方法体的方法
    • 抽象类中一般有抽象方法,也可以没有抽象方法
    • 普通类中,不能有抽象方法
  • 抽象类不能被实例化(不能被 new
  • 抽象类可以被普通类继承 extends
    • 如果普通类继承抽象类,必须实现抽象类中所有的抽象方法
  • 抽象类可以充当接口被实现(implements
    • 如果把抽象类当做接口实现的话,普通类必须得实现抽象类里面定义的所有属性和方法
abstract class Phone {
  // 声明抽象方法
  void processor(); // 手机处理器
  void camera(); // 摄像头

  void info() {
    print("我是抽象类中的普通方法");
  }
}

class Xiaomi extends Phone {
  // 普通类继承抽象类,必须实现抽象类中所有的抽象方法
  void processor() {
    print("骁龙888");
  }

  void camera() {
    print("三星");
  }

  // 普通类中不能有抽象方法
  // void aaa();
}

class Huawei extends Phone {
  // 普通类继承抽象类,必须实现抽象类中所有的抽象方法
  void processor() {
    print("麒麟990");
  }

  void camera() {
    print("徕卡摄像头");
  }

  void system() {
    print("Harmony");
  }
}

void main(List<String> args) {
  // 抽象类不能被实例化
  // var p1 = new Phone();

  Xiaomi m = new Xiaomi();
  m.processor(); // 骁龙888
  m.camera(); // 三星
  m.info(); // 我是抽象类中的普通方法

  Huawei h = new Huawei();
  h.processor(); // 麒麟990
  h.camera(); // 徕卡摄像头
  h.system(); // Harmony
  h.info(); // 我是抽象类中的普通方法
}

接口

  • 接口在dart中就是一个类(只是用法不同)
    • 接口可以是任意类,但一般使用抽象类做接口
  • 一个类可以实现(implements) 多个接口,多个接口用 , 分隔
    • class MyClass implements Interface1, Interface2{...}
  • 普通类实现接口后,必须重写接口中所有的属性和方法
// 手机的处理器
abstract class Processor {
  String cores; // 内核数
  arch(String name); // 制程
}

abstract class Camera {
  String resolution; // 分辨率
  brand(String name); // 品牌
}

class Phone implements Processor, Camera {
  @override
  String cores;

  @override
  String resolution;

  Phone(this.cores, this.resolution);

  @override
  arch(String name) {
    print("芯片制程: " + name);
  }

  @override
  brand(String name) {
    print("相机品牌: " + name);
  }
}

void main(List<String> args) {
  Phone p = new Phone('4', '300w');
  p.arch('5nm'); // 芯片制程: 5nm
  p.brand('徕卡卡'); // 相机品牌: 徕卡卡
}

混入(Mixin)

  • 混入是一段公共代码,混入有两种声明方式:

    • 将类当做混入 class MixinA {...}
      • 作为 Mixin 的类只能继承自 Object,不能继承其他类
      • 作为 Mixin 的类不能有构造函数
    • 通过 mixin 关键字声明: mixin MixinB{...}
  • 混入可以提高代码复用效率,普通类可以通过 with 来使用混入 class MyClass with MixinA, MixinB {...}

  • 使用多个混入时,后引入的混入会覆盖之前混入中的重复内容

    • MixinAMininB 中都有 hello() 方法,MyClass 会使用 MixinB 中的
class Father {}

// MixinA 用作混入,不能继承除Object的其他类
// class MixinA extends Father { 错
class MixinA {
  String name = "MixinA";

  // 用作混入的类,不能有构造函数
  //MixinA();

  void printA() {
    print("A");
  }

  void run() {
    print('A is running.');
  }
}

mixin MixinB {
  String name = "MixinB";

  void printB() {
    print("B");
  }

  void run() {
    print('B is running.');
  }
}

class MyClass with MixinA, MixinB {}

void main(List<String> args) {
  var c = new MyClass();
  c.printA(); // A
  c.printB(); // B

  // 后引入的混入会覆盖前引入的重复内容
  print(c.name); // MixinB
  c.run(); // B is running
}

泛型

泛型实在函数、类、接口中指定宽泛数据类型的语法

  • 泛型函数
  • 泛型类
  • 泛型接口

在尖括号中,使用一个字母来代表类型(E、T、S、K、V)

泛型函数

基本语法:

返回类型 函数名 <输入类型> (参数类型 参数){
  函数体;
}

T getData<T>(T value) {
  return value;
}

getData<int>(12); -> 12
getData<String>("Hi"); -> Hi

示例:

T getData<T>(T value) {
  return value;
}

// 不指定类型的函数:无法明确返回类型
getData2(value) {
  return value;
}

// 只约定参数类型,不约定函数返回值的类型
getData3<T>(T value) {
  return value; 
}

void main(List<String> args) {
  print(getData("Hi")); // Hi
  print(getData(233)); // 233
  print(getData<int>(233)); // 233

  print(getData2(233)); // 233
}

泛型类

class CommonClass {
  Set s = new Set<int>(); // 限制添加类型

  void add(int value) {
    this.s.add(value);
  }

  void info() {
    print(this.s);
  }
}

class GenericsClass<T> {
  Set s = new Set<T>(); // 限制添加类型

  void add(int value) {
    this.s.add(value);
  }

  void info() {
    print(this.s);
  }
}

void main(List<String> args) {
  CommonClass c = new CommonClass();
  c.add(1);
  // c.add("2");
  c.info(); // {1}

  GenericsClass g = new GenericsClass<int>();
  g.add(1);
  // g.add("2");
  g.info(); // {1}

  Set s = new Set(); // 默认是<dynamic> 可以传入任何东西
  s.add(2);
  s.add("1");

  Set ss = new Set<String>();
  // ss.add(1); 报错
  ss.add("value");

  // 字面量形式的泛型
  Set sss = <int>{};
}

泛型接口

// 下面两端代码看起来很重复
abstract class ObjectCache {
  getByKey(String key);
  void setByKey(String key, Object value);
}

abstract class StringCache {
  getByKey(String key);
  void setByKey(String key, String value);
}

// 泛型接口
abstract class Cache<T> {
  getByKey(String key);
  void setByKey(String key, T value);
}

// 文件缓存
class FileCache<T> implements Cache<T> {
  @override
  getByKey(String key) {
    return null;
  }

  @override
  void setByKey(String key, T value) {
    print("文件缓存: key=$key, value=$value");
  }
}

// 文件缓存
class MemoryCache<T> implements Cache<T> {
  @override
  getByKey(String key) {
    return null;
  }

  @override
  void setByKey(String key, T value) {
    print("内存缓存: key=$key, value=$value");
  }
}

void main(List<String> args) {
  // 文件缓存 字符串
  FileCache fc = new FileCache<String>();
  fc.setByKey('foo', 'bar'); // 文件缓存: key=foo, value=bar
  // fc.setByKey("key", 2); 报错

  // 文件缓存 Map
  FileCache fc2 = new FileCache<Map>();
  fc2.setByKey("A", {
    "name": "Rika",
    "age": 233333
  }); // 文件缓存: key=A, value={name: Rika, age: 233333}

  // 内存缓存 字符串
  MemoryCache mc = new MemoryCache<String>();
  mc.setByKey("S", "Adas"); // 内存缓存: key=S, value=Adas
}

使用泛型限制参数类型

class SomeBaseClass {}

class AnotherClass {}

class Child extends SomeBaseClass {}

class Foo<T extends SomeBaseClass> {
  String toString() =>
      "Instance of 'Foo<$T>'"; // Instance of 'Foo<SomeBaseClass>'
}

void main(List<String> args) {
  var someBaseClassFoo = Foo<SomeBaseClass>();
  print(someBaseClassFoo);

  // var anotherClassFoo = Foo<AnotherClass>(); // 类型不对,不符合绑定的要求

  var foo = Foo<Child>(); // 可以继承子类
  print(foo.toString()); // Instance of 'Foo<Child>'

  var foo2 = Foo(); // 默认 someBaseClass
  print(foo2.toString()); // Instance of 'Foo<Child>'
}

枚举

enum

eg. enum Color {red, green, blue}

  • 通过 .values 获得所有枚举值的列表
    • List<Color> colors = Color.values;
  • 通过具体值的 .index 取得索引
    • assert(Color.green.index == 1);
enum Color { red, green, blue }

void main(List<String> args) {
  print(Color.green.index); // 1

  print(Color.values); // [Color.red, Color.green, Color.blue]

  List<Color> colors = Color.values;
  print(colors); // [Color.red, Color.green, Color.blue]

  // 通过下标访问
  print(colors[0]); // Color.red

  // forEach 遍历
  colors.forEach((element) {
    print('values: $element, index: ${element.index}');
  }); // values: Color.red, index: 0 \n values: Color.green, index: 1 \n values: Color.blue, index: 2
}

库与生态

(Pub)[https://pub.dev/]

自定义库

语法:`import '库的位置/库的名称.dart'

./lib/MyCustom.dart

// library MyCustom; // 可省略,建议小写字母+写划线
library my_custom;

class MyCustom {
  String name = "MyCustom";

  static num version = 1.0;

  void info() {
    print("$MyCustom");
  }
}

./custom.dart

import 'lib/MyCustom.dart';

void main(List<String> args) {
  MyCustom mc = new MyCustom();

  mc.info(); // MyCustom
  print(MyCustom.version); // 1.0
}

系统库

语法:import 'dart:库的名称'

import 'dart:math';
import 'dart:core'; // 默认引入,写不写无所谓

void main(List<String> args) {
  print(pi); // 3.141592653589793
  print(max(6, 6));
}

引入部分库

  • 包含引入 (show)
  • 排除引入 (hide)

./lib/common.dart

void f1() {
  print("f1 is running");
}

void f2() {
  print("f2 is running");
}

void f3() {
  print("f3 is running");
}

./show.dart

import 'lib/common.dart' show f1, f3;

void main(List<String> args) {
  f1();
  // f2();
  f3();
}

./hide.dart

import 'lib/common.dart' hide f1, f3;

void main(List<String> args) {
  // f1();
  f2();
  // f3();
}

命名冲突

当库名冲突时,可以通过 as 关键字,给库声明一个前缀

复用上面例子的 common.dart

./lib/function.dart:

void f1() {
  print("f1 is running");
}

void hello() {
  print("Hello");
}

./as.dart

import 'lib/common.dart';
import 'lib/function.dart' as func;

void main(List<String> args) {
  f1(); // f1 is running // common.dart 中的
  func.f1(); // f1 is running // function.dart 中的
}

延迟引入(懒加载)

关键字: deferred as

./deferred.dart

import 'lib/function.dart' deferred as func;

void main(List<String> args) {
  // func.hello(); // 没有加载,无法使用

  print(1);
  greet();
  print(2);
  print(3);

  // 1 2 3 Hello
}

Future greet() async {
  await func.loadLibrary(); //加载 func
  func.hello();
}

通过 part 与 part of 来组装库

part

./lib/phone/Camera.dart

// 与主库建立联系
part of phone;

class Camera {
  String name = "摄像头";

  void info() {
    print("摄像头:卡擦");
  }
}

./lib/phone/Processor.dart

// 与主库建立联系
part of phone;

class Processor {
  String name = "处理器";

  void info() {
    print("处理器:碰擦");
  }
}

./lib/phone/main.dart

library phone;

import 'dart:math';

// 分库
part 'Camera.dart';
part 'Processor.dart';

void main(List<String> args) {
  Camera c = new Camera();
  c.info(); // 摄像头:卡擦

  Processor p = new Processor();
  p.info();

  print(pi);
}

./part.dart

import 'lib/phone/main.dart' as phone;

void main(List<String> args) {
  phone.Camera c = new phone.Camera();
  c.info(); // 摄像头:卡擦
}

系统库

import 'dart:xxx';

dart:core 中的日期

void main(List<String> args) {
  var now = new DateTime.now();
  print(now); // 2023-01-13 15:41:30.653591

  var d = new DateTime(2022, 12, 12, 22, 33, 0);
  print(d); // 2022-12-12 22:33:00.000

  var d1 = DateTime.parse("2022-01-01 12:12:12");
  print(d1); // 2022-01-01 12:12:12.000

  var d2 = DateTime.parse("2022-01-01 12:21:21+0800");
  print(d2); // 2022-01-01 04:12:12.000Z

  // 时间增量
  print(now.add(new Duration(hours: -3))); // 2023-01-13 12:43:51.788021

  // 时间比较
  print(d1.isAfter(d2)); // false
  print(d1.isBefore(d2)); // true
  print(d1.isAtSameMomentAs(d2)); // false

  print(d2.difference(d1)); // 0:09:09.000000

  print(now.month.toString().padLeft(2, '0')); // 01
}
posted @ 2023-01-13 17:23  Zinc233  阅读(38)  评论(0编辑  收藏  举报