Dart中的const定义一个编译时常量。编译时常量是指,在编译时该const值必须确定。而final是指变量的值只能被设置一次。可以是在运行时确定。
示例1:
// 正确 final time = new DateTime.now(); // 错误。因为 DateTime.now()在运行时才能获知时间,在编译时无法获知。 const time = new DateTime.now();const创建的常量是深度递归的,例如常量容器也内元素也是不能变的,但是final修饰的容器内元素可以改变。
示例:
const foo1 = [1,2]; foo1[0]=3; // 错误 foo1.add(3); // 错误 const foo1 = [1,2]; foo1[0]=3; // 正确 foo1.add(3); // 正确相同的const对象只会被创建一次,而相同的final对象被创建多个。
const 可以用来申明常量变量,也可以创建常量值。
示例:
var foo = const[1,2,3]; // const修饰的是[1,2,3],所以[1,2,3] 不能更改,但是foo指向的对象可以更改。 foo=[1,2]; //正确 const foo = [1,2,3]; // 这表示修饰的foo是一个常量变量,不可更改foo指代的对象。 // 根据语法可推测此时[1,2,3] 中的内容是可以更改,但是实际上 foo[1] = 4; // 错误 // const foo = [1,2,3]; 等于 const foo = const [1,2,3]; // 可以认为const foo =xxx 时 const foo = const xxx 的简写形式 // final 修饰的变量所指代的对象是可以更改对象的内容,但是不能更改变量指代的对象。 所以可以认为以下两句是等价的: final foo = const [1,2,3]; const foo = [1,2,3];mixin是一个类,其他类可以访问mixin类中的方法、成员变量而不必继承该类。
mixin是一个普通的类(在Dart语言中该类没有构造方法),其他类可以继承该类中的方法和变量而不用继承整个类。
在java语言中,类的继承关系是单继承的,可以extends一个父类并可以implements多个接口。在Dart中也同样采用这种继承关系。只不过Dart中没有interface 关键字,只有普通类和抽象类。当一个类被作为接口使用,那么在子类中必须重写这个接口类中的所有方法,而不管该方法是否是抽象方法。
所以当有如下继承关系时:
我们在不用Mixin的情况下,java代码的写法和Dart写法基本一致。
// 使用接口的语法 // 动物 abstract class Animal { void live(); } // 水生动物 class Aquatic extends Animal { @override void live() { print("我生活在水中!"); } } // 陆生动物 class Terrestrial extends Animal { @override void live() { print("我生活在陆地!"); } } // 定义接口----------------- abstract class Flyable { void fly() { print("我可以飞行!"); } } abstract class Swimmable { void swim() { print("我可以游泳!"); } } abstract class Singable { void sing(); } // ------------------------ //鲨鱼 class Shark extends Aquatic implements Swimmable { @override void swim() { print('我可以游泳'); } } // 章鱼 class Octopus extends Aquatic implements Swimmable { @override void swim() { print('我可以游泳'); } } // 猫 class Cat extends Terrestrial implements Singable { @override void sing() { print('喵喵叫!'); } } // 鸟 class Bird extends Terrestrial implements Singable, Flyable { @override void sing() { print("啾啾叫!"); } @override void fly() { print("我可以飞行!"); } } main(List<String> args) { Shark().swim(); Octopus().swim(); Cat().sing(); Bird().sing(); Bird().fly(); } /* 我可以游泳 我可以游泳 喵喵叫! 啾啾叫! */上述代码中,虽然abstract类中已经对方法提供了实现,但是当该抽象类作为接口使用,子类必须重写该接口中的所有方法。观察上述代码Swimmable 接在,在实现该接口的Shark 和 Octopus类中, 重新的swim方法完全一样,造成了代码的冗余, 此时可以使用Mixin来来解决该问题 , 将以上代码修改:
// 使用mixin // 动物 abstract class Animal { void live(); } // 水生动物 class Aquatic extends Animal { @override void live() { print("我生活在水中!"); } } // 陆生动物 class Terrestrial extends Animal { @override void live() { print("我生活在陆地!"); } } // 定义接口----------------- mixin Flyable { void fly() { print("我可以飞行!"); } } mixin Swimmable { void swim() { print("我可以游泳!"); } } abstract class Singable { void sing(); } // ------------------------ //鲨鱼 class Shark extends Aquatic with Swimmable { } // 章鱼 class Octopus extends Aquatic with Swimmable { } // 猫 class Cat extends Terrestrial implements Singable { @override void sing() { print('喵喵叫!'); } } // 鸟 // with 关键字必须在implements前面 class Bird extends Terrestrial with Flyable implements Singable { @override void sing() { print("啾啾叫!"); } } main(List<String> args) { Shark().swim(); Octopus().swim(); Cat().sing(); Bird().sing(); Bird().fly(); }上述代码中我将Flyable, 和 Swimmable 改为了mixin . 此时(使用with关键字来继承mixin类) 子类中不必重写mixin中已经实现的方法.
为啥不用extends方式继承? 因为extends是单继承的, 使用with可以加入多个混合类.
上述示例中可以看出mixin的优点:
解决了extends不能多继承问题解决了接口继承方式子类必须重写接口中所有方法(不管是否是抽象方法)的缺点.mixin的继承顺序, 当with关键字后继承多个mixin类且这些类有相同的方法时:
子类中的重写版with关键字后的多个mixin类的最后一个.其实mixin其继承原理,并不是只继了最后一个版本,而是按照顺序继承, 例如: class C with A, B{} 其继承关系是 C->B->A, 即当A,B,C中有同名方法时, B中方法覆盖A中的,C中方法覆盖B中的.
例如:
mixin A { void test() { print("Im class A"); } } mixin B { void test() { print("Im class B"); } } class C with A, B {} main(List<String> args) { C().test(); } // 输出: // Im class B示例:
abstract class Base { void test() { print("Im class Base"); } } mixin A on Base { void test() { print("Im class A"); super.test(); } } mixin B on Base { void test() { print("Im class B"); super.test(); } } class C extends Base with A, B { @override void test() { print("Im class C"); super.test(); } } main(List<String> args) { C().test(); } // 输出: //Im class C //Im class B //Im class A //Im class Baseon 关键字在 mixin中的使用, 声明mixin类时, 可以使用on 关键字来申明该mixin类可以在哪个类的子类中使用.
abstract class Base { void say() { print("Im base class"); } } mixin MixFun on Base { void test() { print('Im Mixin funcion'); } } // 正确, 因为BaseExt是Base类的子类, MixFun可以混入 class BaseExt extends Base with MixFun { } // 报错!!, 因为 ObjTest不是Base以及Base的子类. class ObjTest with MixFun { }Future表示不能立即完成的计算。普通函数返回结果,异步函数返回Future,后者最终将包含结果。当结果准备好时,未来会告诉你。 Stream是异步事件的序列。它就像一个异步迭代,当你请求它时,流不是得到下一个事件,而是告诉你当它准备好时有一个事件。
总结:dynamic 与object 的最大的区别是在静态类型检查上。
1.Runes
Runes是TUF-32字符集。在Dart语言中的String是使用UTF-16表示的。所以UTF-32需要使用专门的类来表达。比如\u{1f600}代表笑脸emoji。
main(List<String> args) { var s = '\u{1f600}'; print(s.runtimeType); // String print(s.runes.runtimeType); // Runes print(s); //输出:😀。 说明String在输出时自动转码 print(s.runes); //输出:(128512) .输出的是编码值 // 创建一个Runes对象 Runes runes = Runes('\u{2665} , \u{1f605}, \u{1f60e}'); print(runes); // 输出:(9829, 32, 44, 32, 128517, 44, 32, 128526) // 输出字符 print(String.fromCharCodes(runes)); // 输出:♥ , 😅, 😎 }此外, Runes继承自Iterable , 所以Runes是课迭代对象。
2.Symbols 官网解释很模糊。只说用到的机会很少。然后就没了。。。
此教程中有一个在反射机制上的应用。
反射机制加载类,需要使用Symbol类作为参数。以下为示例: 首先定义一个library:
library fool_lib; class Foo { f1() { print("function 1"); } f2() { print("function 2"); } f3() { print("Function 3"); } }使用反射机制加载该library
import 'dart:mirrors'; import 'fool_lib.dart'; main(List<String> args) { Symbol lib = Symbol("fool_lib"); Symbol className = Symbol("Foo"); if (checkIfClassAvailableInLibary(lib, className)) print("class fount"); } bool checkIfClassAvailableInLibary(Symbol lib, Symbol className) { MirrorSystem mirrorSystem = currentMirrorSystem(); LibraryMirror libMirror = mirrorSystem.findLibrary(lib); if (libMirror != null) { print("找到模块"); print("${libMirror.declarations.length}"); libMirror.declarations.forEach((key, value) { print(key); }); } if (libMirror.declarations.containsKey(className)) { return true; } return false; }get和set方法为类的成员属性提供了读和写的方法. 每个类的实例其实都包含了隐式的get和set. 同时我们也可以显示定义get和set方法. 使用get和set方法定义的成员变量无法在类的构造函数中赋值.
例如:
class A{ set a(int v) => a=v; int get a => a; A(this.a); // 报错 }get的使用示例:
class Vehicle { String make; String model; int manufactureYear; int vehicleAge; String color; Map<String,dynamic> get map { return { "make": make, "model": model, "manufactureYear":manufactureYear, "color": color, }; } int get age { return DateTime.now().year - manufactureYear; } void set age(int currentYear) { vehicleAge = currentYear - manufactureYear; } Vehicle({this.make,this.model,this.manufactureYear,this.color,}); } main(){ Vehicle car = Vehicle( make:"Honda", model: "Civic", manufactureYear:2010, color: "red", ); print(car.age);// 输出车龄 print(car.map);// 将car的属性组装成map输出 }Dart语言的构造函数有4种:
普通构造函数命名构造函数常量构造函数工厂构造函数 默认构造函数如果定义了类,而没有提供构造函数,那么它有一个默认无参数的构造函数.
2.普通构造函数
class A{ num x,y; A(num x, num y){ this.x=x; this.y=y; } A(this.x, this.y); // 上述构造函数可以简写为此形式, 此为dart语言的语法糖 }3.命名构造函数
Dart语言的普通构造函数不能重载,当需要有多个构造函数时,需要使用命名构造函数. 此外,命名构造函数不能被继承.
class A{ num x,y; A(this,x,this,y); A.origin(){ x=0; y=0; } // 匿名构造函数. }4.之类构造调用父类构造
之类的构造函数中必须调用父类的构造函数. 当我们没有显示调用父类的构造函数时, 系统默认调用父类的父类构造函数.
构造函数的执行顺序(和c++的顺序相同): 初始化列表->父类构造->子类构造;
当父类没有无参数的构造函数时,此时我们必须显示的调用父类的构造函数.
class A{ num x,y; A(this.x,this.y); } class B extends A{ num z; B(num x, num y, this.z):super(x,y); // 调用父类构造 B(num x, num y, num z): z=z, super(x,y) ; // 此构造方法等同与上一个, //需要注意的是: 1. 初始化列表中属性初始化使用=,而父类构造使用(). 2. 父类的构函数必须放在初始化列表的最后 B(num x, num y, num z):super(x,y) , z=z,; // 这样写将会报错 } main(){ var b = B(1,2,3); print(b.x); print(b.y); print(b.z); }5.构造函数传递 类中可以有一个普通构造函数和多个命名构造函数. 构造函数之间可以传递.
class Point { num x, y; // 普通构造函数 Point(this.x, this.y); // 初始化列表中调用普通构造函数 Point.alongXAxis(num x) : this(x, 0); }6.常量构造函数
常量构造函数中所有的成员变量都是final.创建常量对象时需要加const修饰,否则创建的是非const对象.7.工厂构造方法
工厂构造函数是一种构造函数,与普通构造函数不同,工厂函数不会自动生成实例,而是通过代码来决定返回的实例对象.
class Logger { final String name; bool mute = false; // _cache is library-private, thanks to // the _ in front of its name. static final Map<String, Logger> _cache = <String, Logger>{}; factory Logger(String name) { return _cache.putIfAbsent( name, () => Logger._internal(name)); } factory Logger.fromJson(Map<String, Object> json) { return Logger(json['name'].toString()); } Logger._internal(this.name); void log(String msg) { if (!mute) print(msg); } }