1、字符串插值(String interpolation)
要将一个表达式的值放入字符串中,可以使用字符串插值${expression}
main() {
// 字符串插值
print(stringify(1, 3));
}
String stringify(int x, int y) {
return '$x $y';
}
2、null感知运算符(Null-aware operators)
(1)如果一个值可能为null,可以使用??=
赋值表达式,如果当前变量是null,则给变量赋一个值,注意只要变量还是null,即使再次重新使用 ??=
运算符,还是第一次的值,除非中间有过不为null的值,再次使用的时候才会进行更换
int a; // 初始化值为null
a ??= 3;
print(a); // 打印值为3
a ??= 5;
print(a); // 还是3
int a; // 初始化值为null
a ??= 3;
print(a); // 打印值为3
a = 1; // a此时不为null
a = null; // 将a再次赋为null
a ??= 5;
print(a); // 这是打印为5
(2)另一种运算符为??
,如果运算符左边为null,则返回表达式右边的值
print(1 ?? 3); // 打印为1
print(null ?? 12); // 打印为12
3、条件属性访问(Conditional property access)
如果访问对象的属性或者方法可能为null,使用?.
myObject?.someProperty
// 上边等价于
(myObject != null) ? myObject.someProperty : null
// 可以用链式调用
myObject?.someProperty?.someMethod()
// 如果myObject为null或者myObject.someProperty为null,则表达式结果为null且someMethod()方法不会调用
String upperCaseIt(String str) {
return str?.toUpperCase();
}
4、集合字面量(Collection literals)
final aListOfStrings = ['one', 'two', 'three'];
final aSetOfStrings = {'one', 'two', 'three'};
final aMapOfStringsToInts = {
'one': 1,
'two': 2,
'three': 3,
};
以上类型推断为List<String>、Set<String>、Map<String, int>类型,也可以明确指出你要的类型
final aListOfInts = <int>[];
final aSetOfInts = <int>{};
final aMapOfIntToDouble = <int, double>{};
当你用子类去初始化一个列表的时候,但仍然希望列表是List<BaseType>,这时候明确类型是很方便的,如:
final aListOfBaseType = <BaseType>[SubType(), SubType()];
5、箭头语法(Arrow syntax)
箭头函数是一种定义函数的方法,该函数在其右侧执行表达式并返回它的值
var list = <String>["abc", "cbs", "dsa"];
bool hasEmpty = list.any((element) {
return element.isEmpty;
});
// 等价于
bool hasEmpty = list.any((element) => element.isEmpty);
print(hasEmpty);
6、级联(Cascades)
// 级联
var myObject = MyObject();
myObject.someMethod();
// 以上调用myObject的someMethod()方法,表达式的结果是返回someMethod()的值
// 查看数据类型
print(myObject.runtimeType.toString()); // MyObject
var test = myObject..someMethod();
// 以上尽管仍然是调用myObject的someMethod()方法,但结果已经不再是方法的返回值了,结果是myObject的一个引用。
// 查看数据类型
print(test.runtimeType.toString()); // MyObject
使用级联,可以将需要单独语句的操作链接在一起
var button = querySelector("#confirm");
button.text = 'Confirm';
button.classes.add('important');
button.onClick.listen((event) => window.alert('Confirm!'));
// 使用级联上边的代码会变得更短,而且也不再需要button变量
querySelector("#confirm")
..text = 'Confirm'
..classes.add('important')
..onClick.listen((event) => window.alert('Confirm!'));
7、Getters and setters
当你需要对属性进行比简单字段所允许的更多的控制的时候,可以使用getters和setters
例如可以保证设置的属性是否符合某个要求
class MyClass {
int _aProperty = 0;
int get aProperty => _aProperty;
set aProperty(int value) {
if (value > 0) {
_aProperty = value;
}
}
}
var myClass = MyClass();
myClass.aProperty = -10;
print(myClass.aProperty); // 0,不符合set的条件,打印出来的是初始值
myClass.aProperty = 10;
print(myClass.aProperty); // 10,符合set的条件,属性值进行了设置
注意:以上事示例中,如果先设置aProperty的值为10,后设置aProperty为-10,则打印出来的结果为10,10.这是因为最后一次赋值为-10没有满足条件,使用的是上一次赋值的结果10.
也可以使用getter方法去定义一个计算属性
class MyClass2 {
List<int> _values = [];
void addValue(int value) {
return _values.add(value);
}
// 一个计算属性
int get count => _values.length;
}
var myClass2 = MyClass2();
myClass2.addValue(1);
myClass2.addValue(3);
print(myClass2.count); // 2
myClass2.addValue(4);
print(myClass2.count); // 3
再举一个购物车的例子
var shoppingCart = ShoppingCart();
shoppingCart.prices = [10, 20, 50];
// shoppingCart.prices = [10, -20, 50]; 注意会抛出异常
print(shoppingCart.total); // 打印80.0
class ShoppingCart {
List<double> _prices = [];
// List的fold方法可以实现给一个初始值,然后迭代数组中的每一个元素
double get total =>
_prices.fold(0, (previousValue, element) => previousValue + element);
set prices(List<double> value) {
if (value.any((element) => element < 0)) {
throw Exception();
}
_prices = value;
}
}
8、可选位置参数(Optional positional parameters)
Dart有两种函数参数:位置参数和命名参数
位置参数的使用如下:
int sumUp(int a, int b, int c) {
return a + b + c;
}
int total = sumUp(1, 2, 3);
在Dart中可以让位置参数用中括号包裹成为可选参数
int sumUpThree(int a, [int b, int c]) {
int sum = a;
if (b != null) sum += b;
if (c != null) sum += c;
return sum;
}
int totalThree = sumUpThree(10);
print(totalThree); // 10
totalThree = sumUpThree(10, 3);
print(totalThree); // 13
totalThree = sumUpThree(10, 3, 5);
print(totalThree); // 18
可选位置参数一般位于函数参数列表的最后,它的默认值是null,可以为可选位置参数提供默认值,如:
int sumUpThreeWithDefault(int a, [int b = 1, int c = 2]) {
return a + b + c;
}
int totalThreeWithDefault = sumUpThreeWithDefault(1);
print(totalThreeWithDefault); // 4
9、可选命名参数(Optional named parameters)
使用大括号可以定义带名称的命名可选参数
void printName(String firstName, String lastName, {String suffix}) {
print('$firstName $lastName ${suffix ?? ''}');
}
printName('Han', 'Qiao'); // Han Qiao
printName('Han', 'Qiao', suffix: 'A'); // Han Qiao A
printName('Han', 'Qiao', suffix: null); // Han Qiao
命名可选参数默认值为null,可以为可选命名提供默认值
void printName(String firstName, String lastName, {String suffix}) {
print('$firstName $lastName ${suffix ?? ''}');
}
一个函数不能既有可选位置参数又有可选命名参数
10、异常(Exception)
Dart代码可以抛出和捕获异常。与Java相反,Dart的所有异常都是未检查的异常。方法不声明它们可能抛出哪些异常,也不需要捕获任何异常。
Dart提供了Exception
和Error
类型,但你可以抛出任何非空对象:
throw Exception('Something bad happened.');
throw 'Waaaaaaah!';
在处理异常的时候使用try
, on
和catch
关键字
try {
breedMoreLlamas();
} on OutOfLlamasException {
// A specific exception
buyMoreLlamas();
} on Exception catch (e) {
// Anything else that is an exception
print('Unknown exception: $e');
} catch (e) {
// No specified type, handles all
print('Something really unknown: $e');
}
try
关键字的工作方式与大多数其他语言一样。使用on
关键字按类型筛选特定的异常,使用catch
关键字获得对异常对象的引用。
如果不能完全的处理异常,可以使用rethrow
关键字传递异常
try {
breedMoreLlamas();
} catch (e) {
print('I was just trying to breed llamas!.');
rethrow;
}
无论是否抛出异常都要执行代码,使用finally
关键字
try {
breedMoreLlamas();
} catch (e) {
// ... handle exception ...
} finally {
// Always clean up, even if an exception is thrown.
cleanLlamaStalls();
}
再举一个示例:
typedef VoidFunction = void Function();
class ExceptionWithMessage {
final String message;
const ExceptionWithMessage(this.message);
}
abstract class Logger {
void logException(Type t, [String msg]);
void doneLogging();
}
void tryFunction(VoidFunction untrustworthy, Logger logger) {
try {
untrustworthy();
} on ExceptionWithMessage catch (e) {
logger.logException(e.runtimeType, e.message);
} on Exception {
logger.logException(Exception);
} finally {
logger.doneLogging();
}
}
11、在构造函数中使用this(Using this in a constructor)
Dart为构造函数中的属性赋值提供了一个方便的快捷方式,在声明构造函数时使用this.propertyName
。
这个也适用于命名参数,属性名称成为命名参数。
对于可选参数,默认值在这里也同样试用
class MyColor {
int red;
int green;
int blue;
// 使用位置参数
MyColor(this.red, this.green, this.blue);
// 使用可选命名参数
MyColor({this.red, this.green, this.blue});
// 使用带默认值的可选命名参数
MyColor({this.red = 0, this.green = 0, this.blue = 0});
// 使用可选位置参数
MyColor([this.red = 0, this.green = 0, this.blue = 0]);
}
// 使用位置参数调用
final color = MyColor(80, 80, 100);
// 使用可选命名参数调用
final color = MyColor(red: 80, green: 80, blue: 100);
// 使用可选位置参数调用
final color = MyColor(80, 80, 100);
12、初始化列表(Initializer list)
有时当你实现一个构造函数时,你需要在构造函数体执行之前做一些设置。例如,最终发字段在构造函数体执行之前必须有值。在构造函数的命名和它的主体之间的初始化列表中做这些工作:
Point.fromJson(Map<String, num> json)
: x = json['x'],
y = json['y'] {
print('In Point.fromJson(): ($x, $y)');
}
初始化列表的位置也可以放置assert,这个在开发过程中运行,例如:
NonNegativePoint(this.x, this.y)
: assert(x >= 0),
assert(y >= 0) {
print('I just made a NonNegativePoint: ($x, $y)');
}
13、命名构造函数(Named constructors)
为了允许类有多个构造函数,Dart支持命名构造函数:
class Point {
double x, y;
Point(this.x, this.y);
Point.origin() {
x = 0;
y = 0;
}
}
final myPoint = Point.origin();
14、工厂构造函数(Factory constructors)
Dart支持工厂构造函数,它可以返回子类型,甚至返回null。要创建工厂构造函数,使用factory
关键字:
class Square extends Shape {}
class Circle extends Shape {}
class Shape {
Shape();
factory Shape.fromTypeName(String typeName) {
if (typeName == 'square') return Square();
if (typeName == 'circle') return Circle();
print('I don\'t recognize $typeName');
return null;
}
}
15、重定向构造函数(Redirecting constructors)
有时,构造函数的唯一目的是重定向到同一个类中的另一个构造函数。重定向构造函数的函数体是空的,构造函数调用出现在冒号(:)之后。
class Automobile {
String make;
String model;
int mpg;
// The main constructor for this class.
Automobile(this.make, this.model, this.mpg);
// Delegates to the main constructor.
Automobile.hybrid(String make, String model) : this(make, model, 60);
// Delegates to a named constructor
Automobile.fancyHybrid() : this.hybrid('Futurecar', 'Mark 2');
}
再比如:
class Color {
int red;
int green;
int blue;
Color(this.red, this.green, this.blue);
// Create a named constructor called "black" here and redirect it
// to call the existing constructor
Color.black() : this(0, 0, 0);
}
16、常量构造函数(Const constructors)
如果类生成的对象永远不变,则可以将这些对象设置为编译时常量。为此,定义一个const构造函数并确保所有实例变量都是final变量。
class ImmutablePoint {
const ImmutablePoint(this.x, this.y);
final int x;
final int y;
static const ImmutablePoint origin = ImmutablePoint(0, 0);
}