类
命名规则:
dart中使用小写+下划线来命名库和文件名;
首字母大写来命名类的名称;
首单词小写,再加驼峰来命名其他标识符(常量)。dart中是没有类似java中public、protect、private这样的声明、关键字的。使用_开始命名的类、变量就是java中的private
文件aaa.dart
class Point {
int a = 0;
int _b = 0;
}
文件ccc.dart
class _Point2 {
int a = 0;
int _b = 0;
}
文件bbb.dart
import 'aaa.dart';
void main() {
var point = Point();
var aa = point.a; // 可以直接能够访问到类中的变量。
// var bb = point._b; // 报错,不能在另一个文件中调用带下划线的变量
// var point = _Point2(); // 报错,带下划线的类是私有类,不能被调用
}
1.1 构造函数
-
构造函数语法糖
class Point { int a = 0; int _b = 0; Point(this.a, this._b); // 这种形式的构造方法称之为构造函数语法糖 }
-
命名构造函数
class Point { int a = 0; int _b = 0; Point(this.a, this._b); // 构造方法1 // 那如果我们需要带有一个参数的构造方法怎么办呢? // Point(this.a); // 报错,这种写法是不行的 // 那这里能用可选位置参数和可选命名参数来完成构造方法的重载功能吗? // Point([this.a, this._b]); // 报错,不行的。 // Point({this.a, this._b}); // 报错,都是不行的 // 那我们怎么来这种功能呢? Point.ccc(this._b); // 构造方法2。通过类名+.任意名字的这种方式来完成构造方法的重载,来突破dart中不允许方法重载带来的限制。这就是命名构造函数 }
构造方法的使用
void main() { var point = Point(1, 2); var point2 = Point.ccc(2); // 调用命名构造函数 }
-
参数初始化列表
作用:初始化类当中的属性
class Point { int a = 0; int _b = 0; // 这种构造方法就是参数初始化列表。在冒号之后进行初始化 // 在命名构造函数传入一个map,a的值则默认为map集合中,key为1的value;b的值为key为2的value Point.fromMap(Map map) : a = map[1], _b = map[2] {} Point.fromMap2(Map map) : a = map[1], _b = map[2];// 可以不写方法体 }
参数初始化列表的使用
void main() { // 传入一个map集合。还记得之前学生map集合么。冒号前面是key,冒号后面是value var point3 = Point.fromMap({1: 2, 2: 3}); // 打印的日志:point3 a=2 _b=3 print("point3 a=${point3.a} _b=${point3._b}"); }
-
重定向构造函数
重定向构造函数,就是一个构造函数调用另一个构造函数
class Student { String aa = ""; String _bb = ""; String cc = ""; Student(this.aa, this._bb); Student.aaa(aa) : this(aa, "14岁"); Student.bbb(this.aa, this._bb, this.cc); Student.ccc() : this.bbb("王五", "14岁", "男"); // 报错,调用的函数不能是 }
重定向构造函数的使用
void main() { var stu = Student("张三", "11岁"); print('stu aa=${stu.aa} bb=${stu._bb} cc=${stu.cc}'); // 打印结果:stu aa=张三 bb=11岁 cc= var stu2 = Student.aaa("李四"); print('stu2 aa=${stu2.aa} bb=${stu2._bb} cc=${stu2.cc}'); // 打印结果:stu2 aa=李四 bb=14岁 cc= var stu3 = Student.ccc(); print('stu3 aa=${stu3.aa} bb=${stu3._bb} cc=${stu3.cc}'); // 打印结果:stu3 aa=王五 bb=14岁 cc=男 }
-
常量构造函数
属性是用final来修饰的,并且用const修饰的构造方法叫做常量构造方法
class Teacher { final int aa; final int bb; const Teacher(this.aa, this.bb); }
常量构造函数的使用
void main() { // 使用new 来创建常量构造方法的对象就和普通的对象没有区别 var tea = new Teacher(1, 2); var tea2 = Teacher(2, 3); // new可以省略 print(tea == tea2); // 打印结果:false // 使用const 来创建常量构造方法的对象,传递的参数也一样,表示这个对象是同一个编译期常量对象 var tea3 = const Teacher(1, 2); var tea4 = const Teacher(1, 2); print(tea3 == tea4); // 打印结果:true }
-
工厂构造函数
用factory修饰的构造方法
class Persion { // 必须返回一个自己的实例对象,或者自己子类的对象 // factory Persion() { // return new Persion(); // 不能直接new一个对象,会递归,Stack Overflow // } // 创建一个命名构造函数的工厂方法 factory Persion.create() { // return Persion(); // 返回自己子类的对象 return Persion(); // 返回自己默认的构造函数 } // 默认构造函数 Persion(); }
class Stu extends Persion{ }
用工厂构造函数构建一个单例
class School { static School? school; // 最新的版本中必须进行初始化,或者增加?,这个跟kotlin中的类似,可以为null factory School.create() { school ??= School._get(); // 如果school == null,才去执行等号后面的代码,否则不执行 return school!; // 返回唯一实例。dart中!标识不为null,kotlin中是用!!来表示的。 } // School(); // 不能直接用默认构造函数,外界还是能调用 //_School(); // 也不能使用_,构造函数不支持。 School._get(); // 可以使用_创建一个私有的命名构造函数。外界不能再调用了 }
1.2 Getter与Setter
每一个实例属性 非私有变量都会有一个隐式的get(),非final还有一个隐式的set()。这和kotlin中类似
class Student {
int? a;
// 私有的变量,需要自己写
int? _x;
// get方法:x只是方法名,可以随便定义。一般为了方便,就定义成与变量名一样的
int? get x {
return _x;
}
// 如果只有一行代码,可以简写
int? get xx => _x;
// set方法,x2同样是方法名,可以随便定义。
set x2(int x) {
_x = x;
}
// 如果只有一行代码,可以简写
set xx2(int x) => _x = x;
}
方法调用:
void main() {
var stu = Student();
stu.a; // 实际调用的是stu.getA();
stu.a = 10; // 实际调用的是stu.setA(10);
}
1.3 操作符重载
// 操作符重载,重新定义操作符,+ - * / 等。只是改变了实现方式,但优先级还是一样的。
class Point {
int x = 0;
// 在操作符前面加上operator关键字,代表重载操作符。
Point operator +(Point p2) {
var p3 = Point();
p3.x = x + p2.x;
return p3;
}
// 传入参数和返回参数可以是任意对象。
String operator -(int i) {
return "重载的结果是$i";
}
}
void main() {
var p1 = Point();
p1.x = 1;
var p2 = Point();
p2.x = 10;
var p3 = p1 + p2;
print("p3.x=${p3.x}"); // 结果是 p3.x=11。
var t = p1 - 10;
print("t=${t}"); // 结果是 t=重载的结果是10
}
1.4 抽象类与接口
抽象类,同样是用abstract来定义的
abstract class A {
void aa() {}
// 抽象方法,不需要abstract来修饰,加了反而报错。这应该是和java中的抽象类的唯一区别了。
void aaa();
}
接口。在dart中,没有interface关键字。除了抽象类,其他类都可以被当做接口来实现。
class B {
void bb() {
print('我是B的bb方法');
}
}
// 非抽象类既可以被实现,也可以被继承。区别是,实现必须重写它的方法
class C extends B {}
class D implements B {
@override
void bb() {}
}
和java中一样,单继承多实现。
class E {}
class D extends A implements B, E {
@override
void bb() {}
@override
void aaa() {}
}
call方法。这相当于dart中的一个快捷调用
class AA {
call() {}
}
void main() {
var aa = AA();
aa(); // 只要一个类中存在方法名为call的方法,就可以直接这样调用。相当于省略了一个调用
aa.call(); // 当然也可以直接调用。这个跟上面的写法没有任何区别
}
1.5 混合mixins
使用with关键字进行混合,可以使用A类和B类中的方法弥补了不能多继承的遗憾。
但有一个要求,A类和B类中不能有构造方法。否则报错
class A {
void aa() {
print('我是A类的aa方法');
}
}
class B {
void bb() {
print('我是B类的bb方法');
}
}
// 使用with进行混合,C就是一个A、B的混合类
class C with A, B {
void cc() {
print('我是C类的cc方法');
}
}
// 如果C类没有单独的方法,可以进行简写
class C1 = Object with A, B;
使用:
void main() {
var c = C();
c.aa();
c.bb();
c.cc();
}
如果混合的两个类中,A类和B类有相同的方法名的方法aa,那就看后混合的是哪个,调用的是后混合的方法。这边调用的就是B。如果 with B, A ,那调用的就是A类的方法。
如果C类中本身就有aa方法,那调用就是A的aa方法。如果想调用AA中的,可以用super
// 抽象类也可以混合,但必须重写抽象类的抽象方法
class C2 with A, D {
void cc() {
print('我是C2类的cc方法');
}
@override
void dddd() {
print('我是C2类的dddd方法');
}
}
abstract class D {
void aa() {
print('我是D类的dd方法');
}
void dddd();
}
网友评论