重要的概念
-
任何保存在变量中的都是一个 对象 , 并且所有的对象都是对应一个 类 的实例。 无论是数字,函数和
null
都是对象。所有对象继承自 Object 类。 -
尽管 Dart 是强类型的,但是 Dart 可以推断类型,所以类型注释是可选的。
var number = 42;
,number
被推断为int
类型。 如果要明确说明不需要任何类型, 需要使用特殊类型dynamic
。 -
Dart 支持泛型,如
List <int>
(整数列表)或List <dynamic>
(任何类型的对象列表)。 -
Dart 支持顶级函数(例如
main()
), 同样函数绑定在类或对象上(分别是 静态函数 和 实例函数 )。 以及支持函数内创建函数 ( 嵌套 或 局部函数 ) 。 -
类似地, Dart 支持顶级 变量 , 同样变量绑定在类或对象上(静态变量和实例变量)。 实例变量有时称为字段或属性。
-
与 Java 不同,Dart 没有关键字 “public” , “protected” 和 “private” 。 如果标识符以下划线(_)开头,则它相对于库是私有的(私有方法必须要抽离在单独文件中,否则不生效。)。 有关更多信息,参考 库和可见性。
-
标识符 以字母或下划线(_)开头,后跟任意字母和数字组合。
-
Dart 语法中包含 表达式( expressions )(有运行时值)和 语句( statements )(没有运行时值)。 例如,条件表达式
condition ? expr1 : expr2
的值可能是expr1
或expr2
。 将其与 if-else 语句 相比较,if-else 语句没有值。 一条语句通常包含一个或多个表达式,相反表达式不能直接包含语句。 -
Dart 工具提示两种类型问题:警告和错误。 警告只是表明代码可能无法正常工作,但不会阻止程序的执行。 错误可能是编译时错误或者运行时错误。 编译时错误会阻止代码的执行; 运行时错误会导致代码在执行过程中引发 [异常](#exception)。
语法
变量与常量
1. 变量初始化
// 没有明确类型,编译的时候根据值明确类型
var name = 'Bob';
dynamic name = 'Bob';
Object name = 'Bob'
// 显示声明将被推断类型, 可以使用String显示声明字符串类型
String name = 'Bob' ;
2. 默认值
未初始化的变量默认值是 null。即使变量是数字 类型默认值也是 null,因为在 Dart 中一切都是对象,数字类型 也不例外。
3. Object 和 dynamic
dynamic a = 'string';
a = 10;
a = 1.1;
a.foo();
Object b = 'string';
b = 10;
b = 1.1;
// 编译错误 "The method 'foo' isn't defined for the type 'Object'."
// b.foo();
- dynamic和object类型是可以变的
- Object 是静态类型检测的,所以Object 对象只能调用Object 的方法,调用其他方法会产生编译错误
- dynamic是运行时检测的,所以可以调用任何方法,(注意会产生运行时错误,尽量避免使用)
4. Final 和 Const
- 使用过程中从来不会被修改的变量, 可以使用 final 或 const
- 实例变量可以是 final 类型但不能是 const 类型。 必须在构造函数体执行之前初始化 final 实例变量
- Final 变量的值只能被设置一次, 最高级 final 变量或类变量在第一次使用时被初始化(运行时)
class Name {
// 1. 直接设置默认值
final a = 10;
// 2. 在初始化列表中初始化
final a;
Name(this.a)
}
- Const 变量在编译时就已经固定 (Const 变量 是隐式 Final 的类型.)
- Const 变量必须由常量初始化
var a = 10;
const b = 10;
// 编译报错 "Const variables must be initialized with a constant value."
const c = a;
const d = b;
- 在集合字面量之前添加 const 关键字,可以定义编译时常量
var a = const [1, 2, 3];
var b = const {1, 2, 3};
var c = const {1: 1, 2: 2};
内建类型
- Dart 语言支持以下内建类型:
- Number
- String
- Boolean
- List (也被称为 Array)
- Map
- Set
- Rune (用于在字符串中表示 Unicode 字符)
- Symbol
1. Number
- Dart 语言的 Number 有两种类型: int double
- int 整数值不大于64位, 具体取决于平台
- double 64位(双精度)浮点数
2. String
- Dart 字符串是一组 UTF-16 单元序列。 字符串通过单引号或者双引号创建。
- 字符串可以通过 ${expression} 的方式内嵌表达式。 如果表达式是一个标识符,则 {} 可以省略。
- Flutter中字符串格式化只有插值,可以借助第三方库
sprintf
来实现格式化字符串
3. Boolean
- Dart 使用 bool 类型表示布尔值。 Dart 只有字面量 true and false 是布尔类型, 这两个对象都是编译时常量。
- Dart 的类型安全意味着不能使用 if (nonbooleanValue) 或者 assert (nonbooleanValue)。
3. Set
var names = Set();
var names = Set<String>();
var names = <String>{};
// var names = {}; // 这样会创建一个 Map ,而不是 Set 。
函数
函数也是对象,并且有它的类型 Function。这也意味着函数可以被赋值给变量或者作为参数传递给其他函数。 也可以把 Dart 类的实例当做方法来调用。
- 如果函数中只有一句表达式,可以使用简写语法:
bool isNoble(int atomicNumber) => _nobleGases[atomicNumber] != null;
- 在箭头 (=>) 和分号 (;) 之间只能使用一个 表达式 ,不能是 语句。
- 函数有两种参数类型: required 和 optional。 required 类型参数在参数最前面, 随后是 optional 类型参数。 命名的可选参数也可以标记为 “@ required”
- 所有函数都有返回值,没有明确指定返回值,函数隐式添加
return null;
1. 命名可选参数
- 定义函数是,使用 {param1, param2, …} 来指定命名参数
void enableFlags({bool bold, bool hidden}) {...}
- 调用函数时,可以使用指定命名参数 paramName: value,
enableFlags(bold: true, hidden: false);
2. 位置可选参数
- 定义函数是,使用 [param1, param2, …] 来指定位置可选参数
void enableFlags([bool bold, bool hidden]) {...}
- 调用函数, 自动按顺序将数据赋值给位置参数
enableFlags(true, true);
3. 默认参数值
在定义方法的时候,可以使用 = 来定义可选参数的默认值。 默认值只能是编译时常量。 如果没有提供默认值,则默认值为 null。
- 可选参数才能设置默认值
// 位置可选
void enableFlags([bool bold = false, bool hidden = false]) { ... }
// 命名可选
void enableFlags({bool bold = false, bool hidden = false}) { ... }
- 位置可选参数和命名可选参数不能混用。
4. 匿名函数(block)
// 1
var testFunc = (String a) { ... };
// 2
Function(String a) testFunc;
testFunc = (String a) { ... };
// 3
bool Function(String a) testFunc;
testFunc = (String a) { ... };
// 4
typedef MyFunc = bool Function(String a);
MyFunc testFunc;
运算符
- 级联运算符 (..) 可以实现对同一个对像进行一系列的操作。 除了调用函数, 还可以访问同一对象上的字段属性。 这通常可以节省创建临时变量的步骤, 同时编写出更流畅的代码。
querySelector('#confirm') // 获取对象。
..text = 'Confirm' // 调用成员变量。
..classes.add('important')
..onClick.listen((e) => window.alert('Confirmed!'));
- switch 和 case
在非空 case必须加break、continue、thow或return
空case允许程序以 fall-through 的形式执行
非空case 实现 fall-through 需要使用 continue 语句结合 lable 的方式实现:
var command = 'CLOSED';
switch (command) {
case 'CLOSED':
executeClosed();
continue nowClosed;
// Continues executing at the nowClosed label.
nowClosed:
case 'NOW_CLOSED':
// Runs for both CLOSED and NOW_CLOSED.
executeNowClosed();
break;
}
异常
- Dart 中的所有异常是非检查异常。 方法不会声明它们抛出的异常, 也不要求捕获任何异常。
- Dart 提供了 Exception 和 Error 类型, 以及一些子类型。 当然也可以定义自己的异常类型。 但是,此外 Dart 程序可以抛出任何非 null 对象, 不仅限 Exception 和 Error 对象。
// 抛出异常
throw FormatException('Expected at least 1 section');
throw 'Out of llamas!';
捕获
try {
// ···
} on Exception catch (e) {
print('Exception details:\n $e');
} catch (e, s) {
print('Exception details:\n $e');
print('Stack trace:\n $s');
} finally {
// Always clean up, even if an exception is thrown.
cleanLlamaStalls();
}
- 捕获语句中可以同时使用 on 和 catch ,也可以单独分开使用。 使用 on 来指定异常类型, 使用 catch 来 捕获异常对象。
-
catch()
函数可以指定1到2个参数, 第一个参数为抛出的异常对象, 第二个为堆栈信息 ( 一个 StackTrace 对象 )。
类
1. 构造函数
- 通过 构造函数 创建对象。 构造函数的名字可以是 ClassName 或者 ClassName.identifier。
class Point {
final x;
final y;
Point(this.x, this.y);
Point.fromJson(this.x, this.y);
}
var p1 = Point(2, 2);
var p2 = Point.fromJson({'x': 1, 'y': 2});
- 构造函数实例变量赋值简写
class Point {
num x, y;
// 1 手动初始化属性
Point(num x, num y) {
// 还有更好的方式来实现下面代码,敬请关注。
this.x = x;
this.y = y;
}
// 2 自动初始化属性
Point(this.x, this.y)
// 3. 初始化列表
Point(int x, int y) : this.x = x, this.y = y;
}
- 在没有声明构造函数的情况下, Dart 会提供一个默认的构造函数。 默认构造函数没有参数并会调用父类的无参构造函数。
- 子类不会继承父类的构造函数。 子类不声明构造函数,那么它就只有默认构造函数 (匿名,没有参数) 。
- 使用命名构造函数可为一个类实现多个构造函数, 也可以使用命名构造函数来更清晰的表明函数意图
- 常量构造函数
const Point.ImmutablePoint(this.x, this.y);
var a = const Point.ImmutablePoint(10, 11);
var b = const Point.ImmutablePoint(10, 11);
print(identical(a, b)); // true
包含常量构造函数的类中只能包含final
属性
在常量构造函数前加const
会创建出唯一编译时常量
- 工厂构造函数(可以手动返回一个对象)
当执行构造函数并不总是创建这个类的一个新实例时,则使用 factory 关键字。 例如,一个工厂构造函数可能会返回一个 cache 中的实例, 或者可能返回一个子类的实例。
以下示例演示了从缓存中返回对象的工厂构造函数(工厂构造函数无法访问 this。):
class Logger {
final String name;
bool mute = false;
// 从命名的 _ 可以知,
// _cache 是私有属性。
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);
void log(String msg) {
if (!mute) print(msg);
}
}
使用工厂方法实现单例
Singleton._privateConstructor();
static final Singleton _instance = Singleton._privateConstructor();
factory Singleton(){
return _instance;
}
工厂方法 只是为了手动返回一个实例, 下面两种实现等价。
// 工厂方法 只是为了手动返回一个实例
class Test {
Test._internal();
static final Test _instance = Test._internal();
// 1.
static Test init1() {
return _instance;
}
// 2.
factory Test.init2() {
return _instance;
}
}
2. 调用父类非默认构造函数
默认情况下,子类的构造函数会自动调用父类的默认构造函数(匿名,无参数)。 父类的构造函数在子类构造函数体开始执行的位置被调用。 如果提供了一个 initializer list (初始化参数列表), 则初始化参数列表在父类构造函数执行之前执行。 总之,执行顺序如下(类似于Swift 的两段式初始化):
- initializer list (初始化参数列表)
- superclass’s no-arg constructor (父类的无名构造函数)
- main class’s no-arg constructor (主类的无名构造函数)
如果父类中没有匿名无参的构造函数, 则需要手工调用父类的其他构造函数。 在当前构造函数冒号 (:) 之后,函数体之前,声明调用父类构造函数。
class Person {
String firstName;
Person.fromJson(Map data) {
print('in Person');
}
}
class Employee extends Person {
// Person does not have a default constructor;
// you must call super.fromJson(data).
Employee.fromJson(Map data) : super.fromJson(data) {
print('in Employee');
}
}
3. 实例变量
class Point {
num x; // 声明示例变量 x,初始值为 null 。
num y; // 声明示例变量 y,初始值为 null 。
num z = 0; // 声明示例变量 z,初始值为 0 。
}
- 未初始化实例变量的默认人值为 “null” 。
- 所有实例变量都生成隐式 getter 方法。 非 final 的实例变量同样会生成隐式 setter 方法。 有关更多信息,参考 Getters 和 setters.
- 如果在声明时进行了实例变量的初始化, 那么初始化值会在实例创建时赋值给变量, 该赋值过程在构造函数及其初始化列表执行之前。
4. Getter 和 Setter
class Rectangle {
num left, top, width, height;
Rectangle(this.left, this.top, this.width, this.height);
// 定义两个计算属性: right 和 bottom。
num get right => left + width;
set right(num value) => left = value - width;
num get bottom => top + height;
set bottom(num value) => top = value - height;
}
5. 抽象类
abstract class Doer {
// 定义实例变量和方法 ...
void doSomething(); // 定义一个抽象方法。
}
- 抽象类不能实例化
- 抽象类中包含抽象方法,普通方法。抽象方法必须被重写
6. 隐式接口
在Dart 中没有interface。每个Class都是一个隐式接口。
使用implements
实现接口
// person 类。 隐式接口里面包含了 greet() 方法声明。
class Person {
// 包含在接口里,但只在当前库中可见。
final _name;
// 不包含在接口里,因为这是一个构造函数。
Person(this._name);
// 包含在接口里。
String greet(String who) => 'Hello, $who. I am $_name.';
}
// person 接口的实现。
class Impostor implements Person {
get _name => '';
String greet(String who) => 'Hi $who. Do you know who I am?';
}
String greetBob(Person person) => person.greet('Bob');
void main() {
print(greetBob(Person('Kathy')));
print(greetBob(Impostor()));
}
实现多个接口
class Point implements Comparable, Location {...}
7. 继承
- Dart 是单继承。使用 extends 关键字来创建子类, 使用 super 关键字来引用父类:
class Television {
void turnOn() {
_illuminateDisplay();
_activateIrSensor();
}
// ···
}
class SmartTelevision extends Television {
void turnOn() {
super.turnOn();
_bootNetworkInterface();
_initializeMemory();
_upgradeApps();
}
// ···
}
- 子类可以重写实例方法,getter 和 setter。 可以使用 @override 注解指出想要重写的成员:
class SmartTelevision extends Television {
@override
void turnOn() {...}
// ···
}
8. 为类添加功能: Mixin
Mixin 是复用类代码的一种途径, 复用的类可以在不同层级,之间可以不存在继承关系。
- 通过 with 后面跟一个或多个混入的名称,来 使用 Mixin , 下面的示例演示了两个使用 Mixin 的类:
class Maestro extends Person
with Musical, Aggressive, Demented {
Maestro(String maestroName) {
name = maestroName;
canConduct = true;
}
}
- 通过创建一个继承自 Object 且没有构造函数的类,来 实现 一个 Mixin 。 如果 Mixin 不希望作为常规类被使用,使用关键字 mixin 替换 class 。 例如:
mixin Musical {
bool canPlayPiano = false;
bool canCompose = false;
bool canConduct = false;
void entertainMe() {
if (canPlayPiano) {
print('Playing piano');
} else if (canConduct) {
print('Waving hands');
} else {
print('Humming to self');
}
}
}
- 指定只有某些类型可以使用的 Mixin - 比如, Mixin 可以调用 Mixin 自身没有定义的方法 - 使用 on 来指定可以使用 Mixin 的父类类型:
mixin MusicalPerformer on Musician {
// ···
}
9. noSuchMethod()
当代码尝试使用不存在的方法或实例变量时, 通过重写 noSuchMethod() 方法,来实现检测和应对处理:
class A {
// 如果不重写 noSuchMethod,访问
// 不存在的实例变量时会导致 NoSuchMethodError 错误。
@override
void noSuchMethod(Invocation invocation) {
print('You tried to use a non-existent member: ' +
'${invocation.memberName}');
}
}
除非符合下面的任意一项条件, 否则没有实现的方法不能够被调用:
- receiver 具有 dynamic 的静态类型 。
- receiver 具有静态类型,用于定义为实现的方法 (可以是抽象的), 并且 receiver 的动态类型具有 noSuchMethod() 的实现, 该实现与 Object 类中的实现不同。
枚举
枚举中的每个值都有一个 index getter 方法, 该方法返回值所在枚举类型定义中的位置(从 0 开始)。 例如,第一个枚举值的索引是 0 , 第二个枚举值的索引是 1。
枚举类型具有以下限制:
- 枚举不能被子类化,混合或实现。
- 枚举不能被显式实例化。
网友评论