抽象类
Dart抽象类主要用于定义标准,子类可以继承抽象类,也可以实现抽象类接口
- 抽象类通过abstract关键字来定义
- Dart中的抽象方法不能用abstract声明,Dart中没有方法体的方法我们称为抽象方法。
- 如果子类继承抽象类必须得实现里面的全部抽象方法
- 如果把抽象类当做接口实现的话必须得实现抽象类里面定义的所有属性和方法。
- 抽象类不能被实例化,只有继承它的子类可以
extends抽象类和implements的区别:
- 如果要复用抽象类里面的方法,并且要用抽象方法约束自类的话我们就用extends继承抽象类
- 如果只是把抽象类当做标准的话我们就用implements实现抽象类
案例
- 定义一个Animal类要求它的子类必须包含eat方法
abstract class Animal {
eat(); // 抽象方法
printInfo() {
print("我是抽象类中的普通方法");
}
}
class Dog extends Animal {
@override
eat() {
// TODO: implement eat
print("小狗在吃骨头");
}
}
class Cat extends Animal {
@override
eat() {
// TODO: implement eat
print("小猫在吃鱼");
}
}
void main() {
Dog d = new Dog();
d.eat();
d.printInfo();
Cat c = new Cat();
c.eat();
c.printInfo();
}
输出结果:
小狗在吃骨头
我是抽象类中的普通方法
小猫在吃鱼
我是抽象类中的普通方法
多态
- 允许将子类类型的指针赋值给父类类型的指针,同一个函数调用会有不同的执行效果。
- 子类的实例赋值给父类的引用。
- 多态就是父类定义一个方法不去实现,让继承他的子类去实现,每个子类有不同的表现。
上面的例子就是多态的呈现。
Animal d = new Dog();
d.eat();
d.printInfo();
Animal c = new Cat();
c.eat();
c.printInfo();
接口
和Java一样,dart也有接口,但是和Java还是有区别的。
- 首先,dart的接口没有interface关键字定义接口,而是普通类或抽象类都可以作为接口被实现。
- 同样使用implements关键字进行实现。
- 但是dart的接口有点奇怪,如果实现的类是普通类,会将普通类和抽象中的属性的方法全部需要覆写一遍。
- 而因为抽象类可以定义抽象方法,普通类不可以,所以一般如果要实现像Java接口那样的方式,一般会使用抽象类。
- 建议使用抽象类定义接口。
案例1
- 定义一个Database库,支持MySQL, msSQL, MongoDB
- MySQL, msSQL, Mongo三个类中都有同样的方法
GitHub地址:https://github.com/syh31/dart_db
案例2
- 一个类实现多个接口
abstract class A {
String name;
printA();
}
abstract class B {
int age;
printB();
}
class C implements A,B {
@override
int age;
@override
String name;
@override
printA() {
// TODO: implement printA
print("print A");
}
@override
printB() {
// TODO: implement printB
print("print B");
}
}
void main() {
C c = new C();
c.printA();
c.printB();
}
Mixins
- Mixins的中文意思是混入,就是在类中混入其他功能。
- 在Dart中可以使用mixins实现类似多继承的功能。
因为mixins使用的条件,随着Dart版本一直在变。
Dart2.x中使用mixins的条件:
- 作为mixins的类只能继承自object,不能继承其他类
- 作为mixins的类不能有构造函数
- 一个类可以mixins多个mixins类
- mixins绝不是继承,也不是接口,而是一种全新的特性
class Person {
String name;
num age;
Person(this.name, this.age);
printInfo() {
print("${this.name}---${this.age}");
}
}
class A {
printA() {
String info = 'This is A';
print("Print A");
}
run() {
print("A Run");
}
}
class B {
printB() {
print("Print B");
}
run() {
print("B Run");
}
}
class C extends Person with A, B {
C(String name, num age) : super(name, age);
}
void main() {
var c = new C("张三", 20);
c.printInfo();
c.run();
}
输出结果:
张三—20
B Run
泛型
泛型就是解决类接口方法的复用性,以及对不特定数据类型的支持(类型校验)。
泛型方法
T getData<T>(T value) {
return value;
}
void main() {
print(getData(21));
print(getData("xxx"));
print(getData<String>("你好"));
}
输出结果:
21
xxx
你好
泛型类
List list = new List<String>();
// 错误的写法
// list.add(12);
list.add('你好');
自定义泛型类
class PrintClass<T> {
List list = new List<T>();
void add(T value) {
this.list.add(value);
}
void printInfo() {
for (var i = 0; i < this.list.length; i++) {
print(this.list[i]);
}
}
}
void main() {
PrintClass p1 = new PrintClass<String>();
PrintClass p2 = new PrintClass<int>();
p1.add("ss");
p2.add(1);
p2.add(5);
p1.printInfo();
p2.printInfo();
}
输出结果:
ss
1
5
泛型接口
泛型接口实现数据缓存的功能有:文件缓存和内存缓存。内存缓存和文件缓存按照接口约束实现。
- 定义一个泛型接口约束实现它的子类必须有
getByKey(key)
和setByKey(key,value)
- 要求
setByKey
的时候的value
的类型和实例化子类的时候指定的类型一致
例如,假设你创建了一个用于缓存对象的接口:
abstract class ObjectCache {
Object getByKey(String key);
void setByKey(String key, Object value);
}
后来发现需要一个相同功能的字符串类型接口,因此又创建了另一个接口:
abstract class StringCache {
String getByKey(String key);
void setByKey(String key, String value);
}
后来,又发现需要一个相同功能的数字类型接口 … 这里你应该明白了。
泛型可以省去创建所有这些接口的麻烦。 通过创建一个带有泛型参数的接口,来代替上述接口:
abstract class Cache<T> {
T getByKey(String key);
void setByKey(String key, T value);
}
在上面的代码中,T
是一个备用类型。 这是一个类型占位符,在开发者调用该接口的时候会指定具体类型。
abstract class Cache<T> {
getByKey(String key);
void setByKey(String key, T value);
}
class FileCache<T> implements Cache<T> {
@override
getByKey(String key) {}
@override
void setByKey(String key, T value) {
print("文件缓存:将数据key=${key} value=${value}写入文件中");
}
}
class MemoryCache<T> implements Cache<T> {
@override
getByKey(String key) {}
@override
void setByKey(String key, T value) {
print("内存缓存:将数据key=${key} value=${value}写入内存中");
}
}
void main() {
MemoryCache m1 = new MemoryCache<Map>();
MemoryCache m2 = new MemoryCache<String>();
m1.setByKey("index", {"name": "张三", "age": 32});
m2.setByKey("home", "首页数据");
}
输出结果:
内存缓存:将数据key=index value={name: 张三, age: 32}写入内存中
内存缓存:将数据key=home value=首页数据写入内存中
库
在Dart中,库的使用时通过import
关键字引入的。
library指令可以创建一个库,每个Dart文件都是一个库,即使没有使用library指令来指定。
Dart中的库主要有三种:
-
我们自定义的库
import 'lib/xxx.dart';
-
系统内置库
import 'dart:math'; import 'dart:io'; import 'dart:convert';
-
Pub包管理系统中的库
https://pub.dev/packages
https://pub.flutter-io.cn/packages
https://pub.dartlang.org/flutter/- 需要在自己想项目根目录新建一个
pubspec.yaml
- 在
pubspec.yaml
文件然后配置名称、描述、依赖等信息 - 然后运行
pub get
/flutter pub get
获取包下载到本地 - 项目中引入库
import 'package:http/http.dart' as http;
,看文档使用
- 需要在自己想项目根目录新建一个
系统内置库
pubspec.yaml
name: xxx
description: A Dart pub
dependencies:
http: ^0.12.2
index.dart
import 'dart:io';
import 'dart:convert';
void main() async {
var result = await _getDataFromZhihuAPI();
print(result);
}
// API接口:http://news-at.zhihu.com/api/3/stories/latest
_getDataFromZhihuAPI() async {
// io库中提供的HttpClient对象
// 1、创建HttpClient对象
var httpclient = new HttpClient();
// 2、创建Uri对象
var uri = new Uri.http("news-at.zhihu.com", "api/3/stories/latest");
// 3、发起请求,等待请求
var request = await httpclient.getUrl(uri);
// 4、关闭请求,等待响应
var response = await request.close();
// convert库中的编码转换方法
// 5、解码响应的内容
return await response.transform(utf8.decoder).join();
}
Pub包管理系统
以http包为例:https://pub.dev/packages/http/
import 'dart:convert' as convert;
import 'package:http/http.dart' as http;
void main(List<String> arguments) async {
// 接口地址
var url = 'http://www.phonegap100.com/appapi.php?a=getPortalList&catid=20&page=1';
// Await the http get response, then decode the json-formatted response.
var response = await http.get(url);
if (response.statusCode == 200) {
var jsonResponse = convert.jsonDecode(response.body);
print(jsonResponse);
} else {
print('Request failed with status: ${response.statusCode}.');
}
}
当引入两个库中有相同名称标识符的时候,可以使用as
来对库进行重命名
import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;
Element element1 = new Element(); //Uses Element from lib1.
lib2.Element element2 = new lib2.Element(); // Uses Element from lib2.
部分导入
如果只需要导入库的一部分,有两种模式:
-
只导入需要的部分,使用
show
关键字,如下例子所示:import 'package:lib1/lib1.dart' show foo;
-
隐藏不需要的部分,使用
hide
关键字,如下例子所示:import 'package:lib2/lib2.dart' hide foo;
异步支持
使用 async
和 await
关键字实现异步编程。 可以让你像编写同步代码一样实现异步操作。
这两个关键字的使用只需要记住两点:
- 只有
async
方法才能使用await
关键字调用方法 - 如果调用别的
async
方法必须使用await
关键字
async
是让方法变成异步。
await
是等待异步方法执行完成。
void main() async {
var result = await testAsync();
print(result);
}
testAsync() async {
return "Hello Async";
}
懒加载
懒加载也称为延迟加载,可以在需要的时候再进行加载。懒加载的最大好处是可以减少APP的启动时间。
懒加载使用deferred as
关键字来指定,如下例子所示:
import 'package:deferred/hello.dart' deferred as hello;
当需要使用的时候,需要使用loadLibrary()
方法来加载:
greet() async {
await hello.loadLibrary();
hello.printGreeting();
}