【Flutter第3回】Dart 语法预览

328 阅读5分钟
原文链接: ie8384.com

Hello World

学习每一种语言之前,都是从 Hello World 开始。首先看一下Dart的helloworld

void main() {
  print('Hello, World!');
}

很简单,每个程序都有一个 main() 入口函数,print 就是自带的打印 API。在初学 Dart 之时,可以使用 (dartpad)[dartpad.dartlang.org]来运行 Dart 代码片段。

一、变量

Dart 是类型安全的:它使用静态类型检查运行时检查的组合来确保变量的值始终与变量的静态类型匹配。

1. 类型推断

Dart 定义变量的时候,不需要特别指明类型,因为 Dart 有类型推断功能。

var name = 'Voyager I';
var year = 1977;
var antennaDiameter = 3.7;
var flybyObjects = ['Jupiter', 'Saturn', 'Uranus', 'Neptune'];
var image = {
  'tags': ['saturn'],
  'url': '//path/to/saturn.jpg'
};

当然,也可以特意指明变量的类型。

dynamic name = 'Bob';
Object nameObject = 'Bob';
String nameString = 'Bob';

2. dynamic 和 object 区别

dynamic 和 object 都表示未指明单一类型,但是有区别。Object 只能用来表示接受 Dart 里的所有类型,就像 Java 里的 Object。而 dynamic 表示动态的类型,看一下例子就明白了。

dynamic a;
Object b;

main() {
  a = "";
  b = "";
  printLengths();
}

printLengths() {
  // no warning
  print(a.length);

  // error:
  // The getter 'length' is not defined for the class 'Object'     print(b.length);
}

3. 变量默认值为null

另外,未初始化的变量,都是默认为 null。

4. final 和 const

定义常量的时候,可以使用 final 和 const。 const 是编译时常量,而实例常量,必须使用 final.

5. Dart 支持的类型

下面罗列一下 Dart 支持的类型:

  • numbers: num、int 和 double,num 可以表示 int 和 double
var x = 1;
var hex = 0xDEADBEEF;
var y = 1.1;
var exponents = 1.42e5;
  • strings: String,支持单引号,双引号
var s1 = 'Single quotes work well for string literals.';
var s2 = "Double quotes work just as well.";
var s1 = 'String '
    'concatenation'
    " works even over line breaks.";
  • booleans: boolean
  • lists (also known as arrays):
var list = [1, 2, 3];
assert(list.length == 3);
assert(list[1] == 2);

list[1] = 1;
assert(list[1] == 1);
  • sets:
var elements = <String>{};
elements.add('fluorine');
  • maps:
var nobleGases = {
  2: 'helium',
  10: 'neon',
  18: 'argon',
};
var gifts = Map();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';
  • runes (for expressing Unicode characters in a string):UTF-32编码的字符串。它可以通过文字转换成符号表情或者代表特定的文字。

main() {
  var clapping = '\u{1f44f}';
  print(clapping);
  print(clapping.codeUnits);
  print(clapping.runes.toList());
 
  Runes input = new Runes(
      '\u2665  \u{1f605}  \u{1f60e}  \u{1f47b}  \u{1f596}  \u{1f44d}');
  print(new String.fromCharCodes(input));
  • symbols: 类比js的Symbol,Symbol是compoile-time constants,就是编译时常量。使用也很简单,直接在标识前面加个#。rune 和 synbols 不怎么常用。
const symbol = #symbol;

6. String 和 number 的转化

另外,附上String 和 number 的转化:

// String -> int
var one = int.parse('1');
assert(one == 1);

// String -> double
var onePointOne = double.parse('1.1');
assert(onePointOne == 1.1);

// int -> String
String oneAsString = 1.toString();
assert(oneAsString == '1');

// double -> String
String piAsString = 3.14159.toStringAsFixed(2);
assert(piAsString == '3.14');

二、控制流程语句

1. 常见的控制流程语句

if-else、for 循环、while 和 do-while 循环、Break and continue、switch and case:

f (year >= 2001) {
  print('21st century');
} else if (year >= 1901) {
  print('20th century');
}

for (var object in flybyObjects) {
  print(object);
}

for (int month = 1; month <= 12; month++) {
  print(month);
}

while (year < 2016) {
  year += 1;
}
var command = 'CLOSED';
switch (command) {
  case 'CLOSED': // Empty case falls through.
  case 'NOW_CLOSED':
    // Runs for both CLOSED and NOW_CLOSED.
    executeNowClosed();
    break;
}

2. 数组的 where 和 forEach

where语句用于过滤,forEach用于循环

flybyObjects.where((name) => name.contains('turn')).forEach(print);
flybyObjects.forEach((item){
  print(item)
});

三、Imports

1. import 使用

// Importing core libraries
import 'dart:math';

// Importing libraries from external packages
import 'package:test/test.dart';

// Importing files
import 'path/to/my_other_file.dart';

// Import only foo.
import 'package:lib1/lib1.dart' show foo;

// Import all names EXCEPT foo.
import 'package:lib2/lib2.dart' hide foo;

// 重命名,当有两个类库拥有重名接口时,就可以重命名类库
import 'package:lib2/lib2.dart' as lib2;

2. 懒加载类

首先重命名,然后 调用loadLibrary来加载

import 'package:greetings/hello.dart' deferred as hello;

Future greet() async {
  await hello.loadLibrary();
  hello.printGreeting();
}

四、异步

Future test() async {
  return 'rr';
}
void main() async {
 test()
 .then((name){
  print(a);
 })
 .catchError((e) => print(e));
}

Dart 是基于单线程模型的语言。如果任何代码阻塞线程执行都会导致程序卡死。s

1. isolate

在Dart也有自己的进程(或者叫线程)机制,叫 isolate。APP的启动入口main函数就是一个 isolate 。开发者也可以通过引入import 'dart:isolate'创建自己的 isolate ,对多核 CPU 的特性来说,多个 isolate 可以显著提高运算效率,当然也要适当控制isolate的数量,不应滥用。Dart中isolate之间无法直接共享内存,不同的isolate之间只能通过isolate API进行通信。

2. Future

Future 是 Dart 中提供的一个类,它用于封装一段在将来会被执行的代码逻辑。构造一个 Future 就会向 event queue 中添加一条记录。

3. await 和 async

调用 await 的函数需要加上关键字 async,否则编译会报错。

五、函数

1. 首先看看函数的定义:

bool isNoble(int atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}

2. 返回类型和参数类型都可以省略:

isNoble(atomicNumber) {
  return _nobleGases[atomicNumber] != null;
}

3. 如果函数体只有一行,也可以使用简写=>:

isNoble(atomicNumber) => _nobleGases[atomicNumber] != null;

4. 支持参数默认值,支持可选参数,支持必选参数

isNoble({test, atomicNumber: 'ddd', @required int child}) => print(atomicNumber);

void main() {
   isNoble(test: 'cccs', child: 1);
}

5. 可以把方法做参数

printElement(element) {
  print(element);
}

var list = [1, 2, 3];

// Pass printElement as a parameter.
list.forEach(printElement);

6. 可以把方法赋值给一个变量

var loudify = (msg) => '!!! ${msg.toUpperCase()} !!!';
assert(loudify('hello') == '!!! HELLO !!!');

7. 所有函数都有返回值,可以返回变量、常量和函数。如果没有返回,那么默认是返回null

8. 静态作用域和方法的嵌套

Dart 是静态作用域语言,变量的作用域在写代码的时候就确定过了。基本上大括号里面定义的变量就 只能在大括号里面访问,和 Java 作用域类似。

var topLevel = true;

main() {
  var insideMain = true;

  myFunction() {
    var insideFunction = true;

    nestedFunction() {
      var insideNestedFunction = true;

      assert(topLevel);
      assert(insideMain);
      assert(insideFunction);
      assert(insideNestedFunction);
    }
  }
}

六、级联符号(...)

querySelector('#confirm') // Get an object.
  ..text = 'Confirm' // Use its members.
  ..classes.add('important')
  ..onClick.listen((e) => window.alert('Confirmed!'));

相当于

querySelector('#confirm') // Get an object.
  ..text = 'Confirm' // Use its members.
  ..classes.add('important')
  ..onClick.listen((e) => window.alert('Confirmed!'));

七、类

1. 命名构造函数

class Point {
  num x;
  num y;

  Point(this.x, this.y);

  // 命名构造函数
  Point.fromJson(Map json) {
    x = json['x'];
    y = json['y'];
  }
}

2. 重定向构造函数

class Point {
  num x;
  num y;

  Point(this.x, this.y);                 
  Point.alongXAxis(num x) : this(x, 0); 
}

3. 常量构造函数

如果类生成从来不改变的对象,则可以把这些对象定义为编译器常量。用一个 const 构造函数赋值为实例变量,并将实例变量设置为 final 来实现。

class ImmutablePoint {

  final num x, y;

  const ImmutablePoint(this.x, this.y);
}

void main() {
 
var a = const ImmutablePoint(1, 1);
var b = const ImmutablePoint(1, 1);

print(a == b); 
assert(a == b); 
}

4. 工厂构造函数

工厂构造函数不能使用 this , factory 关键字用于实现构造函数,不是每次都构造新的示例,可以从 cache 里返回,或者返回 子类的实例。

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) {
    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);
  }
}

5. Getters and setters

使用 get 和 set 关键字 来定义 Getters 和 setters:

class Rectangle {
  num left, top, width, height;

  Rectangle(this.left, this.top, this.width, this.height);

  // Define two calculated properties: right and 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;
}

void main() {
  var rect = Rectangle(3, 4, 20, 15);
  assert(rect.left == 3);
  rect.right = 12;
  assert(rect.left == -8);
}

6. 支持 mixin 的继承机制

Dart 使用 mixin 实现多继承:

abstract class 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');
    }
  }
}

class Musician extends Performer with Musical {
  // ···
}

class Maestro extends Person
    with Musical, Aggressive, Demented {
  Maestro(String maestroName) {
    name = maestroName;
    canConduct = true;
  }
}

7. 把类当做方法调用

如果 Dart 类实现了 call() 函数则 可以当做方法来调用。

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');
}

六、Implicit interfaces(隐式接口)

每个类都隐式的定义了一个包含所有实例成员的接口, 并且这个类实现了这个接口。如果你想创建类 A 来支持类 B 的 api,而不想继承 B 的实现,则类 A 应该实现 B 的接口。

// A person. The implicit interface contains greet().
class Person {
  // In the interface, but visible only in this library.
  final _name;

  // Not in the interface, since this is a constructor.
  Person(this._name);

  // In the interface.
  String greet(who) => 'Hello, $who. I am $_name.';
}

// An implementation of the Person interface.
class Imposter implements Person {
  // We have to define this, but we don't use it.
  final _name = "";

  String greet(who) => 'Hi $who. Do you know who I am?';
}

greetBob(Person person) => person.greet('bob');

main() {
  print(greetBob(new Person('kathy')));
  print(greetBob(new Imposter()));
}

// 实现多个接口
class Point implements Comparable, Location {
  // ...
}

七、异常

1. 抛出异常:

if (astronauts == 0) {
  throw StateError('No astronauts.');
}

2. 捕获异常:

try {
  for (var object in flybyObjects) {
    var description = await File('$object.txt').readAsString();
    print(description);
  }
} on IOException catch (e) {
  print('Could not describe object: $e');
} catch (e) {
  print('Could not describe object: $e');
} finally {
  flybyObjects.clear();
}

八、其他

另外,Dart 也支持 泛型、静态函数、静态变量(static)、类型判断(is)、注释(单行//,多行/* */ )。