[Dart] 入门学习
Dart
基于B站视频 Dart 学习
基础
注释
// 单行注释
/* 多行注释 */
/// 文档注释(支持markdown)
变量
万物皆对象,变量名称是引用
声明
- 明确指定类型:
int age = 18
- 不明确类型:
var age = 18
或dynamic 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
浮点数
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
- 单引号、双引号均可
- 三引号可包含换行符
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 = <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"); // ✔
}
类型判断运算符 is
、is!
// 避空运算符
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 {...}
-
使用多个混入时,后引入的混入会覆盖之前混入中的重复内容
- 若
MixinA
和MininB
中都有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 来组装库
./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
}
本文来自博客园,作者:Zinc233,转载请注明原文链接:https://www.cnblogs.com/Zinc233/p/Dart_Study.html