1 构造函数
- 不要键入初始化的形式
- 空的构造函数体使用
;
而不是{}
- 不要使用new
- 不要过多使用const
上面的说法只是规范性,当然你用了也不影响
class Point {
num x, y;
//默认构造函数,只存在一个
Point(this.x, this.y);
}
void main() {
//不要使用new(即使使用并不报错,但并不推荐,因为已经弃用了)
var p = Point(1,2);
}
1.1 默认构造函数
- 默认构造函数只能存在一个
- 无返回值
- 如类无构造函数,则会自动创建一个隐式的无参默认构造函数
注意:是无构造函数,并非无默认构造函数
class Point {
num x, y;
//无构造函数会创建一个默认的无参构造函数 即Point();
}
void main() {
Point p = Point();
}
1.2 命名构造函数
可理解为默认构造函数的重载,可存在多个
class Point {
num x, y;
//命名构造函数
Point.temp(this.x, this.y);
//:后是赋值
Point.temp2(a,b): x=a,y=b;
}
void main() {
Point p = Point.temp(1, 2);
Point p2 = Point.temp2(1,2);
Point p3 = Point(); //错误,无无参默认构造函数
}
1.3 重定向构造函数
重定向构造函数的主体为空,构造函数调用出现在冒号(:)之后
class Point {
num x, y;
Point(this.x, this.y);
Point.alongXAxis(num x) : this(x, 0);
Point.alongXAxis2() : this.alongXAxis(0);
}
1.4 常量构造函数
生成的对象不会改变,所有的属性都是final的
class ImmutablePoint {
static final ImmutablePoint origin =const ImmutablePoint(0, 0);
final num x, y;
const ImmutablePoint(this.x, this.y);
}
1.5 工厂构造函数
工厂构造函数可以从缓存返回实例,也可以返回子类型的实例
-
factory
修饰上述构造函数 , 修饰后需要返回值,返回本类类型 - 内部不能使用this
class Logger {
final String name;
static final Map<String, Logger> _cache = <String, Logger>{};
factory Logger(String name) {
//缓存中有实例对象,则返回,否则创建
if (_cache.containsKey(name)) {
return _cache[name];
} else {
final logger = Logger._internal(name);
_cache[name] = logger;
return logger;
}
}
//命名构造函数
Logger._internal(this.name);
}
2 setter和getter
当定义一个属性时,会自动创建隐式的setter和getter方法,当属性被final或const修饰时,只会创建getter方法
class Box {
num get contents => contents*2; //错误,会重复回调,get中不能再次使用contents
set contents(value) {
contents = value - 1;
}
num get single => 0; //只定义get方法,是没有set方法的,效果等同于final(优点在于,在抽象类中,这种属性可以将属性的值延迟定义在子类实现中)
}
//使用
void main() {
var b = Box();
b.contents; //调用get方法
b.contents = 2; //调用set方法
}
3 继承extends
继承类只能有一个
方法继承
class
继承后方法和属性可重写
abstract class
继承后,其中的抽象方法必须重写(@override
)
mixin
不可继承
构造函数继承
- 继承默认无参构造函数时,可以省略
- 重定向继承的构造函数不能有实体
- 可随意继承,不必对应
- 先调用父类,再调用子类构造函数
class B{
int x;
B(this.x){
print("B $x");
}
B.mm(){
print("B.mm");
}
}
class T extends B{
T(int x): super(x){ //如果super无参数则可以省略
print("T $x");
}
//命名构造函数的继承方式与默认构造函数是一样的
T.mm(): super(1){
print("T.mm");
}
T.nn(): super.mm(){
print("T.mm");
}
T.aa():this.nn(); //重定向构造函数,不能有实体
}
void main() {
T t = T(1); //先打印B 1 ,在打印T 1
}
4 实现implements
- 实现的接口可存在多个
- dart不存在
interface
, 上面3类型都可实现 - 方法都必须重写实现(不会使用接口的方法)
- 属性都必须实现(setter/getter,final只需要getter),向上兼容,且值并不会传递过来
class A{
void b(){
print("A.b");
}
}
abstract class B{
final int x =1;
int y = 1;
void b();
}
class T extends A implements B{
//重写x属性,这里扩展了方法setter。
@override
int x = 2; //向上兼容,反之错误
//final int y = 2; //错误,将y属性已经存在的setter方法去掉并不合理
void printX(){
print("T.x $x"); //打印T.x 2
}
}
void main() {
T().b();
}
5 混合with
这个概念,主要是解决无法继承多个父类的问题
但是,引入多个父类的话,会造成方法和属性重复,这时就要靠with决定
//A中的方法和属性为A和S方法的集合,如果有重复,取S中的
class T = A with S;
//多个就顺序混合,即 (A with B)with S
class T2 = A with B,S;
//顺序混合,即 (A with C)with S
class T3 with A,C,S{}
混合是顺序的,类似于贴纸,最后贴的保存使用
关于super
如T2
中的S内使用super
,那么调用的将是A with B
内的方法或属性,是层级结构的
注意:用于混合的类不能声明构造函数
class S {
int x = 1;
a() {print("S.a");}
//S(); 声明构造函数就不能用于混合
}
class A {
int x = 2;
a(){print("A.a");}
b(){print("A.b");}
}
class B {
int x = 3;
a(){print("B.a");}
b(){print("B.b");}
c(){print("B.c $x");} //S在最末,所以x的值去S中的x
}
mixin C{}
//mixin的继承方式使用on(只能一个父类)
mixin C2 on A{}
////mixin的实现方式使用implements(可以多个接口)
mixin C3 on A implements B,S{}
//方式一
class T = B with A, S;
//方式二,可加入新的方法
class T2 extends B with A, S{
//方法重写,优先级最高
a(){print("T2.a");}
}
//方式三,可直接混合
class T3 with A,C,S{
}
void main() {
T t = T();
//打印顺序为:S.a A.b B.c 3
t.a();
t.b();
t.c();
}
6 可调用的类
实现call()方法可以让你的Dart类像函数一样被调用
class WannabeFunction {
call(String a, String b, String c) => '$a $b $c!';
}
main() {
var wf = new WannabeFunction();
var out = wf("Hi", "there,", "gang");
print('$out');
}
7 元数据
元数据即java
的annotation
注解,但本质是一个常量构造函数
class Todo {
final String who;
final String what;
const Todo(this.who, this.what);
}
@Todo('seth', 'make this do something')
void doSomething() {
print('do something');
}
元数据可以出现在库、类、类型定义、类型参数、构造函数、工厂、函数、字段、参数或变量声明之前,也可以出现在导入或导出指令之前
元数据可配合反射(mirrors)使用
网友评论