一、函数
在Dart中,函数(或方法) 也是对象,它的类型是 Function
。 这意味着,函数可以赋值给变量,也可以当做其他函数的参数。
1.方法定义
在Dart中,类型是可选,可以省略显式的类型,但仍然建议显式指定类型。
返回类型 方法名称(参数1,参数2,......){
方法体
return 返回值;
}
String greet(String name){
return "hello , $name";
}
- 方法中可以定义一个内部方法并调用这个内部方法,该内部方法不能在方法外部调用
- 在Dart中,类型是可选,可以省略显式的类型,但仍然建议显式指定类型。
- 函数也是对象,所有函数都有返回值。没有指定返回值函数返回
null
。使用void
来修饰函数则函数没有返回值
2.方法传参
命名可选参数
与位置可选参数
的区别:前者中的参数与顺序无关,无需按顺序传参,且传参数时需使用冒号;后者与顺序相关,传参必须依照顺序。
1)位置可选参数
在参数中用中括号[ ]包含的参数为位置可选参数:(调用的时候可以带上[ ]中的参数,也可不带,也可以带上其中几个)
String getPersionInfo(String name, [int age, String sex]) {
return "name : $name ; age : $age ; sex : $sex";
}
print(getPersionInfo("name")); // 输出name : name ; age : null ; sex : null
print(getPersionInfo("name", 8)); //输出name : name ; age : 8 ; sex : null
2)命名可选参数
在参数中用{ }包含的参数为命名可选参数
// 当调用方法时没有传入参数sex,则sex默认被赋值为"man"。
String getPersionInfo(String name, {int age, String sex = "man"}) {
return "name : $name ----- age : $age ----- sex : $sex";
}
print(getPersionInfo("Dart", age: 8)); //输出 name : Dart ----- age : 8 ----- sex : man
print(getPersionInfo("Dart", sex: "girl")); //输出 name : Dart ----- age : null ----- sex :girl
print(getPersionInfo("Dart", sex: "girl", age: 8)); //输出 name : Dart ----- age : 8 ----- sex : girl
在{ }外的参数为必传参数,而{ }里的参数为可选参数,即可传可不传,可以不按照{ }里的参数顺序传参,当传{ }中的参数时,应该用参数名:参数值
的方式传递。
3)方法参数
//方法,可作为参数
method1() {
print("I am method one");
}
//参数为方法的函数
method2(f()) {
f();
}
//调用方法method2
method2(method1) ;
I am method one
解释:和匿名方法一样:
var fn=(){
print("一个匿名方法");
};
fn是一个变量,代表着这个(){ print(“一个匿名方法”); };方法,可以直接当作参数传递。
4)required
required翻译成中文的意思是需要、依赖
最开始 @required 是注解,现在已经作为内置修饰符。主要用于允许根据需要标记任何命名参数(函数或类),使得它们不为空。因为可选参数中必须有个 required 参数或者该参数有个默认值。
String printUserInfo(String username, {int age=10, String sex="男"}) {//行参
return "姓名:$username---性别:$sex--年龄:$age";
}
String printInfo(String username, {required int age, required String sex}) {//行参
return "姓名:$username---性别:$sex--年龄:$age";
}
void main(args) {
print(printUserInfo('张三'));
print(printUserInfo('张三',age: 20,sex: "女"));
//age 和 sex必须传入
print(printInfo('张三',age: 22,sex: "女"));
}
//表示 name 和age 是必须传入的命名参数
class Person {
String name;
int age;
Person({required this.name,required this.age}); //表示 name 和age 必须传入
String getName() {
return "${this.name}---${this.age}";
}
}
void main(args) {
Person p=new Person(
name: "张三",
age: 20
);
print(p.getName());
}
// name 可以传入也可以不传入 age必须传入
class Person {
String? name; //可空属性
int age;
Person({this.name,required this.age}); //表示 name 和age 必须传入
String getName() {
return "${this.name}---${this.age}";
}
}
void main(args) {
Person p=new Person(
name: "张三",
age: 20
);
print(p.getName()); //张三---20
Person p1=new Person(
age: 20
);
print(p1.getName()); //null---20
}
4.其他函数
1)箭头函数
当函数体的内容只有一句时,可用箭头指向表示。例如以下函数体可以改为用箭头=>指向:
使用箭头表示前:
// 当数组中的元素大于5,则返回5
List list = [2, 4, 6, 5, 8];
var newList = list.map((e) {
if (e > 5) {
return 5;
}
return e;
});
print(newList.toList());
// [2, 4, 5, 5, 5]
使用箭头表示后:
//使用三目运算,将函数体简化成一句话,输出的结果和上面的例子是一样的
List list = [2, 4, 6, 5, 8];
var newList1 = list.map((e) => e > 5 ? 5 : e);
print(newList1.toList());
// [2, 4, 5, 5, 5]
箭头函数省略了花括号的表达,箭头后面跟一个表达式,函数的返回值也就是这个表达式的值。另外,箭头函数也可以与匿名函数结合,形成匿名箭头函数。
var func = (num x, num y) => x + y;
2)匿名函数
可创建没有名字的函数,称为匿名函数,也被称为lambda表达式或者闭包。 匿名方法可以赋值给一个变量。匿名函数的常用场景:
- 将一个匿名函数赋值给一个变量;
- 在传参的时候,把匿名函数作为参数传递。
// 定义匿名函数,并将其赋值给一个变量func,注意,函数体最后的花括号处必须有分号结束。
var func = (x,y){
return x + y;
};
print(func(10,11)); // 21
var fun = () { //定义了一个变量fun(),这个变量的值是后面的方法体(匿名方法)
print("我是匿名方法");
};
fun(); //在这里fun()是一个变量,在这里使用了这个变量
// 我是匿名方法
注意,匿名函数与普通函数基本相同,也有参数列表,函数体,只是省去了函数名而已。
var fun = (int i) { //定义了一个变量fun,使用这个变量的时候需要带上一个参数
print("我是匿名方法 :$i");
};
fun(2); //这里使用fun(2)这一个变量
// 我是匿名方法 :2
3)自执行方法
顾名思义,自执行方法,即指不需要主动的调用该方法,当程序启动的时候会自动执行该段代码;
((){
//这里输入代码内容
})();
(() {
print("这是一段自执行代码!");
})();
这是一段自执行代码!
在括号里可以定义传入的参数,在第一个括号里定义形参,在最后一个括号里传入实参,如:
((int i) {
print(i);
print("这是一段自执行代码!");
})(50);
// 50
// 这是一段自执行代码!
二、类
Dart是一门面向对象的编程语言,面向对象编程语言的三个基本特征是:封装,继承,多态
- 封装:封装是对象和类的主要特征。封装,把客观事物封装成抽象的类,并且把自己的部分属性和方法提供给其他对象使用。
- 继承: 面向对象编程语言的一个主要功能是“继承”。继承是指该实例化的对象能够使用现有类,以及这个类所继承的类的所有的变量和方法。
- 多态:多态性是允许你将父对象设置成为一个或更多的他的子对象相等的技术。
1.类
1)定义
Dart所有的东西都是对象,所有的对象都是继承自Object类。Dart是单继承的面向对象语言,所有的对象都是类的实例,并且所有的类都是Object的子类。一个类通常由属性和方法组成。
// Dart中定义一个类
class Person {
String name; // 可在数据类型前加late,构造函数中就不用初始化
int age;
Person(String name, int age){
this.name = name;
this.age = age;
}
}
Dart中的类与Java中相似,不同的是Dart中没有private
、public
这些成员访问修饰符。如果是类私有的成员,不希望外面访问,只需要在成员变量之前加上一个下划线_
变为私有即可。
以上代码在Dart中还有一种简化写法,可自动在构造方法中对成员变量初始化。
// Dart中定义一个类
class Person {
String name;
int age;
//在构造方法中初始化成员变量时,可使用如下写法简化
Person(this.name, this.age);
// 如需处理其他变量时,也可单独对其操作
// Person (this.name, this .age,String address){
// print(address):
// }
// 注意,构造方法不能重载,以上注释掉
}
另外还需要注意一点,Dart中没有构造方法的重载,不能写两个同名的构造方法。
2)late
late 关键字主要用于延迟初始化。
class Person {
late String name;
late int age;
void setName(String name, int age) {
this.name = name;
this.age = age;
}
String getName() {
return "${this.name}---${this.age}";
}
}
void main(args) {
Person p = new Person();
p.setName("张三", 20);
print(p.getName());
}
abstract class Db{ //当做接口 接口:就是约定 、规范
late String uri; //数据库的链接地址
add(String data);
}
class Mysql implements Db{
String uri;
Mysql(this.uri);
add(data) {
// TODO: implement add
print('这是mysql的add方法'+data);
}
remove(){
}
}
main() {
Mysql mysql=new Mysql('xxxxxx');
mysql.add('1243214');
}
3)初始化列表
- 初始化列表位于构造方法的小括号与大括号之间,在初始化列表之前需添加一个冒号。
- 初始化列表是由逗号分隔的一些赋值语句组成。
- 适合用来初始化 final修饰的变量
- 初始化列表的调用是在构造方法之前,也就是在类完成实例化之前,因此初始化列表中不能访问 this
import 'dart:math';
class Point {
num x;
num y;
num distance;
Point(x, y)
: x = 4,
y = 5,
distance = sqrt(x * x + y * y) {
print("这是构造方法");
}
}
void main() {
var p = new Point(2, 3);
// 这是构造方法
print(p.distance);
// 3.605551275463989
}
2.方法
1)getter、setter方法
- 在Java中不会直接在类的外部去访问类成员,通常使用setter和getter方法来操作类的成员变量。
- 在Dart中所有类中都包含隐式的getter方法,对于非final修饰的成员,类中还包含隐式的setter方法。
- 既在Dart中可直接在类外部通过**
.
**操作符访问类成员。 - 很多时候调用setter和getter方法并不仅仅是为了赋值和访问,而是为了一些额外的处理,这时只需使用set与get关键字实现setter和getter方法即可。
class Person {
String userName;
Person(this.userName);
// 方法名前加get关键字
String get name {
return "user:" + this.userName;
}
// 方法名前加set关键字
set name(String name) {
// do something
this.userName = name;
}
}
void main() {
var p = new Person("zhangsan");
print(p.name); // user:zhangsan
p.name = "Jack";
print(p.name); // user:Jack
}
在创建对象时new
关键字可以省略不写。在写Flutter界面时,不建议写new
关键字实例化对象,因为Flutter框架中没有类似的xml语言来描述UI界面,界面也是使用Dart语言来写,在使用Dart写UI时保持代码的简洁和结构化,省略new
会更友好。
类中用get修饰的方法块,使用的时候通过调用属性的方式使用。
class Persion {
String _name;
Persion(this._name);
get getName {
return _name;
}
set setName(value) {
_name = value;
}
}
void main() {
Persion man = Persion("深圳"); //实例化一个Persion对象
print(man.getName); //和调用类的属性的方式一样。通过“对象.属性”的方式调用get修饰的方法体
man.setName = "惠州"; //通过“对象.属性 = 值”的方式调用set修饰的方法体
print(man.getName);
}
深圳
惠州
2)构造方法
如果没有定义构造方法则会有一个默认的无参构造方法,并且会调用超类的无参构造方法。
默认构造方法
当实例化对象的时候,会自动调用的函数,构造方法的名称和类的名称相同,在一个类中默认构造方法只能有一个。
class Person{
String name='张三';
int age=20;
//默认构造函数
Person(){
print('这是构造函数里面的内容 这个方法在实例化的时候触发');
}
}
// 最新版本的dart中需要初始化不可为null的实例字段,如果不初始化的话需要在属性前面加上late
class Person{
late String name;
late int age;
//默认构造函数
Person(String name,int age){
this.name=name;
this.age=age;
}
//默认构造函数的简写
// Person(this.name,this.age);
}
class Container {
int width;
int height;
Container({required this.width, required this.height});
}
void main() {
var c1 = new Container(width: 100, height: 100);
var c2 = new Container(width: 100, height: 100);
print(identical(c1, c2)); //false c1和c2在内存中存储了2份
}
命名构造方法
- 当通过指定的命名构造函数实例化对象时,会调用改命名构造方法,命名构造函数可以有多个。
- 命名参数中的必需要传的参数要添加
required
关键字,这样有利于静态代码分析器进行检查;
class Person {
//person为类名
late String name; //属性,命名构造函数不初始化属性时,必须加late,否则无法使用命名构造函数
late int age;
Person(this.name, this.age); //默认构造函数,当实例化一个对象时,会自动调用到该函数
Person.now() {
print("这是一个命名构造方法");
}
getInfo() {
//方法
print("name : $name age : $age");
}
}
void main() {
Person man = new Person("ShenZhen", 40); //实例化对象(调用了默认构造函数)
man.getInfo();
Person man2 = new Person.now(); //实例化对象的时候调用了命名构造函数
}
name : ShenZhen age : 40
这是一个命名构造函数
常量构造方法
提供一个状态永远不变的对像,在Dart中可创建一个编译时常量对象。
class ConstPoint {
final num x;
final num y;
// 使用const修构造方法
const ConstPoint(this.x, this.y);
// 编译时常量对象,需使用const来创建对象
static final ConstPoint origin = const ConstPoint(0, 0);
}
void main() {
print(ConstPoint.origin.x);
// 0
print(ConstPoint.origin.y);
// 0
}
/*
常量构造函数总结如下几点:
1、常量构造函数需以const关键字修饰
2、const构造函数必须用于成员变量都是final的类
3、如果实例化时不加const修饰符,即使调用的是常量构造函数,实例化的对象也不是常量实例
4、实例化常量构造函数的时候,多个地方创建这个对象,如果传入的值相同,只会保留一个对象。
5、Flutter中const 修饰不仅仅是节省组件构建时的内存开销,Flutter 在需要重新构建组件的时候,由于这个组件是不应该改变的,重新构建没有任何意义,因此 Flutter 不会重建构建 const 组件
*/
//常量构造函数
class Container {
final int width;
final int height;
const Container({required this.width, required this.height});
}
void main() {
var c1 = Container(width: 100, height: 100);
var c2 = Container(width: 100, height: 100);
print(identical(c1, c2)); //false
var c3 = const Container(width: 100, height: 100);
var c4 = const Container(width: 100, height: 100);
print(identical(c3, c4)); //true
var c5 = const Container(width: 100, height: 110);
var c6 = const Container(width: 120, height: 100);
print(identical(c5, c6)); //false
}
// 实例化常量构造函数的时候,多个地方创建这个对象,如果传入的值相同,只会保留一个对象。
工厂构造方法
需要创建一个新的对象或者从缓存中取一个对象时可使用工厂构造方法。
class Logger {
final String name;
// 创建一个静态Map做为缓存
static final Map<String, Logger> _cache = <String, Logger>{};
//定义一个命名构造方法,用下划线”_“修饰,将构造方法私有化
Logger._internal(this.name);
// 使用关键字factory修饰类同名构造方法
factory Logger(String name) {
if (_cache.containsKey(name)) {
return _cache[name];
} else {
//调用命名构造方法创建新对象
final logger = new Logger._internal(name);
_cache[name] = logger; // 存入缓存
return logger;
}
}
}
void main() {
var uiLog = new Logger('UI');
var eventLog = new Logger('event');
}
构造方法重定向
有时候一个构造方法会调动类中的其他构造方法来实例化,这时候可以使用构造方法重定向
class Point {
num x;
num y;
// 同名构造方法
Point(this.x,this.y) ;
//命名构造方法重定向到同名构造方法,中间使用一个冒号
Point.alongXAxis(num x) : this(x, 0);
}
3)私有方法 属性
- java等语言中有private、public、proteccted关键字表示属性或方法私有性,而在Dart语言中使用下划线_表示该方法或属性为私有的。
- 只有当类定义在其他独立的文件上时"_"表示私有性才是有效的,若和主入口函数main()在同一个文件下私有性不会生效。
- 如果想要使用类的私有方法或者私有属性,可以通过类中公有方法返回私有属性。如私有属性(私有方法同理):
class Persion {
String _name; //私有属性
Persion(this._name); //公有方法,返回私有属性
getName() {
return _name;
}
}
void main() {
Persion man = new Persion("Dart");
String myName = man.getName(); //Persion类的实例对象通过Persion类的公有方法getName()获取类中的私有属性
print(myName);
}
Dart
4)静态方法 属性
使用static 关键字来实现类级别的变量和函数。静态方法不能访问非静态成员,非静态方法可以访问静态成员
class Person {
static String name = "深圳"; //name为static修饰的静态变量
int age = 0;
static void show() {
print("name : $name");
}
void printInfo() { /*非静态方法可以访问静态成员以及非静态成员*/
print(name); //访问静态属性
print(this.age); //访问非静态属性
show(); //调用静态方法
}
static void printUserInfo() { //静态方法
print(name); //静态属性
show(); //静态方法
// print(this.age); //静态方法没法访问非静态的属性
// this.printInfo(); //静态方法没法访问非静态的方法
// printInfo();
}
}
void main() {
print(Person.name); //使用name这个属性时直接通过“类名.属性”的方式
// 深圳
}
3.对象操作符
- ? 条件运算符
- as 类型转换
- is 类型判断
- … 级联操作
1)条件成员访问符?
- 在Java中很容易碰到恼人的空指针错误,因此在方法调用前需要进行对象的非空判断,这样的判断语句使代码变得冗长,可读性差,不整洁。Dart中则发明了一个新的运算符用于处理此类情况。
- 条件成员访问符
?.
和.
类似,但是运算符左边的对象不能为null
,否则返回null
,若对象不为null
,则返回对象本身。 - 在对象的后面使用
?
判断该对象是否是null
class Persion {
String name = "深圳";
Persion(this.name);
void show() {
print("name : $name");
}
}
void main() {
Persion man; //这里只是定义了一个Persion的对象man,但是没有给man赋值
print(man?.name); //这里原本会报错,使用了条件运算符?判断man是一个空值,故不会打印也不会报错
}
2)类型判断符
使用 is 判断该变量是什么数据类型
Persion man=new Persion("name");
if(man is Persion){ //判断man是否是Persion类型
print("true");
}
// true
使用as进行类型的转换
man as Persion //将对象man转换为Persion对象
3)级联运算符…
使用**.
操作符调用对象在Dart中也支持,Dart另外增加了一种级联运算符..
**,在对象后使用级联符号+属性或方法,会返回对象本身,类似于java中的Builde建造者模式。
级联运算符
可在同一个对象上连续调用多个方法以及访问成员变量。 可避免创建临时变量。
class Persion {
String name ;
int age ;
Persion(this.name, this.age);
void show() {
print("name : $name and age : $age");
}
}
void main() {
Persion man = new Persion("深圳",40);
man..name = "惠州" //使用..name后返回的还是man对象,可以进行接下来..age的操作
..age=50
..show();
}
假如类Person有三个方法,setName
、setAge
、save
,则可如下调用
new Person()..setName("Bob")..setAge(20)..save();
使用级联运算符调用方法,无需该方法返回对象本身即可连续的流式的调用该对象的其他方法。
4)运算符重载
class Point {
int x;
int y;
Point(this.x, this.y);
// 使用operator关键字,为该类重载"+"运算符
Point operator +(Point p) {
return new Point(this.x + p.x, this.y + p.y);
}
//为该类重载"-"运算符
Point operator -(Point p) {
return new Point(this.x - p.x, this.y - p.y);
}
}
void main() {
var p1 = new Point(1, 5);
var p2 = new Point(7, 10);
// 重载运算符后,类可以使用“+”、“_”运算符操作
var p3 = p1 + p2;
var p4 = p2 - p1;
print("${p3.x}, ${p3.y}");
// 8, 15
print("${p4.x}, ${p4.y}");
// 6, 5
}
Dart中允许重载的运算符如下:
+ | ~ | < | == | << |
- | ~/ | > | [] | >> |
* | % | <= | []= | | |
/ | ^ | >= | & |
4.面向对象
1)继承
子类继承父类
一个子类继承自一个父类,那么这个子类的实例化对象直接可以使用这个父类的属性或方法。继承使用关键字extent 。
class 子类 extent 父类{
子类(子类参数1, 子类参数2, ...):super(父类参数1, 父类参数2, ...);
}
class Person {
String name;
int age;
Person(this.name, this.age);
void show() {
print("name : $name and age : $age");
}
}
class Superman extends Person {
//Superman继承Person
late String sex;
Superman(String name, int age, String sex) : super(name, age) {
//super()里的参数是要传递给父类的参数
this.sex = sex;
}
// 也可向命名构造方法传参
// Superman(String name, int age, String sex) : super.xxx(name, age){
// this.sex = sex;
// }
}
void main() {
Superman man = new Superman("深圳", 40, "男"); //Superman实例化对象
man.show(); //Superman实例化的对象可以直接使用父类Person的方法show();
}
// name : 深圳 and age : 40
子类重写父类
在子类中不仅仅可以扩展父类中的属性或者方法,还能重写父类中的方法
class Person {
String name;
int age;
Person(this.name, this.age);
void show() {
print("name : $name and age : $age");
}
}
class Superman extends Person {
Superman(String name, int age) : super(name, age);
// 子类重写父类的标志
void show() {
//在子类中复写了父类中的show方法
print("姓名: $name----年龄:$age");
}
}
void main() {
Superman man = new Superman("深圳", 40);
man.show(); //通过子类的对象调用的是子类中复写的方法
}
// 姓名: 深圳----年龄:40
super调用父类
可以通过super关键字调用父类的方法
class Persion {
String name;
int age;
Persion(this.name, this.age);
void show() {
print("name : $name and age : $age");
}
}
class Superman extends Persion {
Superman(String name, int age) : super(name, age);
void show() {
super.show(); //子类的show()方法通过super.show()的形式调用父类的方法
}
}
void main() {
Superman man = new Superman("深圳", 40);
man.show();
}
// name : 深圳 and age : 40
mixins混入
mixins的中文意思是混入,就是在类中混入其他功能。在Dart中可以使用minxins实现类似多继承的功能。mixins的使用条件随着Dart的版本不断更新而有所改变,此处讲的是Dart2.x中使用minxins的条件:
- 作为minxins的类只能继承自Object,不能继承其他类,不能有构造函数
- 一个类可以minxins多个minxins类
- minxins绝不是继承,也不是接口,而是一种全新的特性
- mixins的类型就是其超类的子类型。
// 首先定义三个父类
class Father1 {
a() {
print("this is a func");
}
common() {
print("common Father1");
}
}
class Father2 {
b() {
print("this is b func");
}
common() {
print("common Father2");
}
}
class Father3 {
c() {
print("this is c func");
}
common() {
print("common Father3");
}
}
//走义子笑
class Son extends Father1 with Father2, Father3 {}
// 等价于class Son with Father1, Father2, Father3 {}
void main() {
var obj = new Son();
obj.common();
// common Father3
obj.a();
// this is a func
obj.b();
// this is b func
obj.c();
// this is c func
}
当两个混合类A和B中有相同的方法,那么C类调用该方法的时候会调用with关键字上最靠后的混合类的方法
class A {
run() { //A类中run()方法
print("run A");
}
doA() {
print("I am A");
}
}
class B {
run() { //B类中run()方法
print("run B");
}
doB() {
print("I am B");
}
}
class C with A, B {}
main() {
C c = new C();
c.doA();
// I am A
c.doB();
// I am B
c.run();
// run B
print(c is A);
// true
print(c is B);
// true
print(c is C);
// true
}
2)抽象类
Dart中的抽象类:
- Dart中的抽象类主要用于定义标准,子类可以继承抽象类,也可以实现抽象类接口。
- 抽象类通过abstract关键字来定义,Dart中没有方法体的方法称之为抽象方法;
- 如果把抽象类当作接口实现必须得实现抽象类里面定义的所有属性和方法;
- 抽象类不能被实例化,只有继承它的子类可以。如果子类继承抽象类必须实现里面的抽象方法;
extends 抽象类 和 implement 的区别:
- 如果要复用抽象类里面的方法,并且要用抽象方法约束自类的话我们就用extend继承抽象类
- 如果只是把抽象类当作标准的话我们就用implement实现抽象类
abstract class Animal { //Animal 为抽象类
eat(); //没有实现方法体,默认是一个抽象方法
}
class Dog extends Animal{
eat() { //如果在Dog类中没有定义eat()方法,将会报错
// do something
}
}
直接通过抽象类进行初始化,会报错
Animal a = new Animal(); //会报错
3)接口
- 在Java 中用interface关键字定义接口,而在Dart语言中普通的类或者抽象类都可以作为接口被实现。同样是通过使用implements关键字实现。
- Dart中如果使用普通类或者抽象类做接口类,实现这个接口类的时候要覆写这个接口类所有属性和方法。抽象类中可以定义抽象方法,故建议使用抽象类定义接口。(接口通常是定义规范)
abstract class Animal { //抽象类,用作接口
String size;
eat() {
//do something
}
}
class Dog implements Animal { //implements 用于实现接口
String size; //需要重新定义属性size
eat() { //需要重新定义方法eat()
// do something
}
}
/*
定义一个DB库 支持 mysql mssql mongodb
mysql mssql mongodb三个类里面都有同样的方法
*/
abstract class Db {
//当做接口 接口:就是约定 、规范
late String uri; //数据库的链接地址
add(String data);
save();
delete();
}
class Mysql implements Db {
String uri;
Mysql(this.uri);
add(data) {
// TODO: implement add
print('这是mysql的add方法:' + data);
}
delete() {
// TODO: implement delete
return null;
}
save() {
// TODO: implement save
return null;
}
remove() {}
}
class MsSql implements Db {
late String uri;
add(String data) {
print('这是mssql的add方法:' + data);
}
delete() {
// TODO: implement delete
return null;
}
save() {
// TODO: implement save
return null;
}
}
main() {
Mysql mysql = new Mysql('xxxxxx');
mysql.add('1243214');
// 这是mysql的add方法:1243214
}
一类多接口
实现多个接口,通过逗号“,”分隔。这个类要实现所有接口的属性和方法。
abstract class A {
String name;
doA(){}
}
abstract class B {
String name;
doB() {}
}
class C implements A,B { //要实现两个类,通过“,”分隔
String name;
doA() {} //不覆写doA()会报错
doB() {} //不覆写doB()会报错
}
/*
Dart中一个类实现多个接口:
*/
abstract class A {
late String name;
printA();
}
abstract class B {
printB();
}
class C implements A, B {
late String name;
printA() {
print('printA');
}
printB() {
// TODO: implement printB
return null;
}
}
void main() {
C c = new C();
c.printA();
}
4)多态
/*
Dart中的多态:
允许将子类类型的指针赋值给父类类型的指针, 同一个函数调用会有不同的执行效果 。
子类的实例赋值给父类的引用。
多态就是父类定义一个方法不去实现,让继承他的子类去实现,每个子类有不同的表现。
*/
abstract class Animal {
eat(); //抽象方法
}
class Dog extends Animal {
eat() {
print('小狗在吃骨头');
}
run() {
print('run');
}
}
class Cat extends Animal {
eat() {
print('小猫在吃老鼠');
}
run() {
print('run');
}
}
main() {
Dog d = new Dog(); // 子类本身赋值可以使用子类任意方法
d.eat();
// 小狗在吃骨头
d.run();
// run
// 子类赋值给父类则只能调用父类中的方法
Animal e = new Dog(); //使用d.eat()的时候会调用Dog类中复写的eat()方法
e.eat();
// 小狗在吃骨头
Animal c = new Cat(); //使用c.eat()的时候会调用Cat类中复写的eat()方法
c.eat();
// 小猫在吃老鼠
}
三、泛型
泛型就是解决类、接口、方法的复用性、以及对不特定数据类型的支持(类型校验)
// 要返回一个数字类型可调用getDate()方法传入数字类型。要返回一个String类型的数据的时候,需要定义一个返回String类型的方法。
getData(int value) {
return value * 2;
}
print(getData(3));
// 6
getData1(String value) {
return value;
}
print(getData1("惠州"));
// 惠州
- 以上代码冗余,两个方法大部分内容相同,只是传入返回数据类型不同。
- 可使用泛型,实现传什么类型就返回什么类型的功能,还支持类型校验。以下“T”为不固定的传入类型:
T getData<T>(T value) { //传入的实参是什么类型,则“T”就代表该类型
return value;
}
print(getData<String>("深圳")); //<String>中的String为检验传的参数是否是String类型,不写则不校验
// 深圳
1.泛型类
当实例化泛型类的时候传入了指定的类型,那么在调用其中该泛型类中的方法时会进行类型校验,只能使用指定的类型。否则将将会报错。
class MyList {
List list = <int>[];
void add(int value) {
this.list.add(value);
}
List getList() {
return list;
}
}
main() {
MyList l = new MyList();
l.add(1);
l.add(12);
l.add(5);
print(l.getList());
// [1, 12, 5]
}
1)不指定类型
class MyList<T> {
List list = <T>[];
void add(T value) {
this.list.add(value);
}
List getList() {
return list;
}
}
main() {
// ListClass list = new ListClass();
MyList list = MyList(); //实例化一个泛型类(这里没有指定类型T的实际类型,因此没有类型校验,传各种类型)
list.add(1);
list.add(2);
list.add(3); //添加int类型数据
list.add("深圳"); //添加String类型数据,不会报错
print(list.getList());
// [1, 2, 3, 深圳]
}
2)指定类型
class MyList<T> {
List list = <T>[];
void add(T value) {
this.list.add(value);
}
List getList() {
return list;
}
}
main() {
MyList l2 = new MyList<String>();
l2.add("张三1");
// l2.add(11); //错误的写法
print(l2.getList());
// [张三1]
MyList l3 = new MyList<int>();
l3.add(11);
l3.add(12);
// l3.add("aaaa"); // 报错
print(l3.getList());
// [11, 12]
}
2.泛型接口
Dart中的泛型接口实现数据缓存的功能:有文件缓存、和内存缓存。内存缓存和文件缓存按照接口约束实现。
// 定义一个泛型接口 约束实现它的子类必须有getByKey(key) 和 setByKey(key,value)。要求setByKey的时候的value的类型和实例化子类的时候指定的类型一致
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> {
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 m=new MemoryCache<String>();
// m.setByKey('index', '首页数据');
MemoryCache m = new MemoryCache<Map>();
m.setByKey('index', {"name": "张三", "age": 20});
}