Flutter开发指南之理论篇:Dart语法01(数据类型,变量,函数)

总目录

Flutter开发指南之理论篇:Dart语法01(数据类型,变量,函数)
Flutter开发指南之理论篇:Dart语法02(运算符,循环,异常)
Flutter开发指南之理论篇:Dart语法03(类,泛型)
Flutter开发指南之理论篇:Dart语法04(库,异步,正则表达式)


 Dart是一门面向对象语言,它针对web 和移动设备开发进行了优化,主要特点为:

  • 一切皆对象!无论是数字,函数还是null,所有对象继承自Object类;
  • 声明一个变量时可以不指定具体类型,Dart可以自动推断类型;
  • Dart支持顶层函数,函数是一等对象,且函数可作为参数传递;
  • Dart使用_开头表示私有属性,没有关键字publicprotectedprivate

1. 数据类型

 在Dart中,支持的数据类型有:NumberStringBooleanListMapSetRune,这些类型都可以被初始化为字面量,它们的主要特点如下:

1.1 Number

 Dart使用int,double和num表示数字类型,其中,num可以是整数或浮点数的类型。

// 整型,整数值不大于64位
var age = 33
int age = 33

// 浮点型,64位双精度浮点数
var salary = 18888.88
double salary = 18888.88

// num是int和double的父类
// 具体类型由dart自动推导
num age = 33
num salary = 18888.88

 字符串与数字类型转换:

// 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.2 String

 Dart的字符串类型的编码格式为UTF-16,通过单引号或者双引号(等价)创建。如果需要实现多行字符串对象,可以连续三个单引号或者三个双引号(等价);或者使用r前缀创建“原始raw”字符串,以保留字符串格式,比如不会对’\n’等进行转移。

  • 创建String
// 普通字符串
var country = 'china'
var country = "china"

// 多行字符串
// 会保持格式,但对于转义字符会进行转义
var comment = '''
    Your point is great.
    I support you!
'''
var comment = """
    Your point is great.
    I support you!
"""
// 使用r前缀,保持字符串原始格式
String news = r"Trump failed, \n biden won!"
  • 判断两个String是否相等
var country1 = 'china'
var country2 = "china"
// 判断两个字符串相等,使用 ==
// 字符串中引入变量值,使用 ${},如果不是表达式可以去掉{}
print("is equal = ${country1==country2}")

1.3 Boolean

 Dart使用bool类型表示布尔值,只有字面量true和false是布尔类型。

var isUsed = false

// 等价于
// bool isUsed = false

1.4 List

 Dart使用List类型表示一个列表,即有序对象的集合。

var list = [1, 2, 3]; 

// 等价于
// List<int> list = [1, 2, 3];

 如果需要定义一个List类型的编译时常量,可以使用const。

var constantList = const [1, 2, 3];
// constantList[1] = 1; // 取消注释会引起错误。

1.5 Set

 Dart使用Set类型表示一个集合,这个集合的元素是唯一。


var countries = {'China', 'America', 'Canada', 'France'}

// 等价于
// Set<String> countries = {'China', 'America', 'Canada', 'France'}

 要创建一个空集,使用前面带有类型参数的{},或者将{}赋值给Set类型的变量。

var countries = <String>{};
// Set<String> names = {}; // 这样也是可以的。
// var names = {}; // 这样会创建一个 Map ,而不是 Set 。

// 添加元素
countries.add('Germany');
countries.addAll(countries);

// 获取长度
var setLength = elements.length

 同样,如果需要定义一个Set类型的编译时常量,可以使用const。

fianl countries = const {'China', 'America', 'Canada', 'France'}

// countries.add('Germany'); 报错

1.6 Map

 Dart使用Map用来关联keys和values的对象(键值对集合),其中,keys 和 values 可以是任何类型的对象,并且在一个Map对象中一个key只能出现一次,但是 value可以出现多次。

// 创建一个空的Map对象
var personInfos = {}
var personInfos = Map()

// 创建一个Map对象,并赋值
var personInfos = {
    'name': 'xijinping',
    'sex': 'male',
    'age': 66
}

var personInfos = Map();
personInfos['name'] = 'xijinping';
personInfos['sex'] = 'male';
personInfos['age'] = '66';

// 创建一个常量Map
final var personInfos = const {
    'name': 'xijinping',
    'sex': 'male',
    'age': 66
}
// personInfos['hometown']='china' 会报错,常量不允许修改

1.7 Rune

 Dart使用Rune来表示字符串中的UTF-32编码字符。由于 Dart 字符串是一系列 UTF-16 编码单元, 因此要在字符串中表示32位Unicode值需要特殊语法支持,Unicode 定义了一个全球的书写系统编码, 系统中使用的所有字母,数字和符号都对应唯一的数值编码。表示 Unicode 编码的常用方法是\uXXXX, 这里 XXXX 是一个4位的16进制数,比如心形符号 (♥) 是\u{2665}。

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

输出结果为:
👏
[55357, 56399]
[128079]
♥ 😅 😎 👻 🖖 👍

2. 变量

2.1 变量

 在dart中,声明一个变量有四种方式: var, dynamic, object具体类型,它们的区别如下:

  • var的类型由dart自动推导,一旦初始化它的数据类型不可变;
  • dynamic动态任意类型,编译阶段不检查,被赋值后仍然可被赋值为其他类型;
  • object动态任意类型,编译阶段检查,被赋值后仍然可被赋值为其他类型;
  • 指定具体的类型,跟Java声明一个变量一样。

示例如下:

var name;
name = 'Bob'; // 类型为String
name = 123;   // 报错,初始化后类型被锁定
// 等价于 var name = 'Bob';
// 等价于 String name = 'Bob';(指定具体类型)

dynamic name;
name = 'Bob'; // 类型为String
name = 123;   // 类型为num
name.test();  // 如果name对象的test()方法不存在,编译阶段不会报错,运行时报错

object name;
name = 'Bob'; // 类型为String
name = 123;   // 类型为num
name.test();  // 如果name对象的test()方法不存在,编译阶段报错

 注:dart中一切皆对象,没有初始化的变量默认值都是null。

2.2 final,const和static

(1)final和const

finalconst均可用于声明一个变量不可变(常量),它们的特点如下:

  • const要求在声明时初始化且初始值必须为编译时常量
  • final在类中声明变量不要求初始化,可以在构造函数参数列表中初始化,初始值也不要求为编译时常量;
  • const,final被初始化后,变量的值将不能被改变,被const声明的变量又被成为常量;
  • const,final均可被用于文件和类中,当const用于声明类成员变量时必须同时被声明为static。

const的使用场景:

// 场景一:定义一个常量
// 在定义时初始化且初始值为编译时常量
// 一旦赋值不允许修改,可以省略变量的类型,如var,int等

void main() {
  const a = 1;             // 数值,字符串等类型常量
  const b = 'hello';
  const c = a;
  
  const b = a > 1 ? 2 : 1; // 表达式常量,所有值都是编译时常量
  
  const a = const [1,2,3]; // 集合常量,=的右边必须用const修饰
  
  const b = ConstObject(2); // 对象常量,对象的构造函数必须用const修饰
  b.log();
}

class ConstObject {
  final value;
  const ConstObject(this.value);
  log() {
    print(value);
  }
}

// 场景二:作为修饰符
// 如果修饰集合,要求集合的每个元素均为编译时常量且不允许更改;
// 如果修饰构造函数,要求该类的所有成员必须使用final声明;

void main() {
  var c = 2;
  var a = const [c,2,3];    //ERROR, 集合元素必须是编译时常数。
  
  const a = const [1,2,3];  
  a[1] = 2;                //ERROR, 不允许修改。
}


class ConstObject {
  
  final value;
  int value2;           //ERROR, 必须是 final 变量。
  
  const ConstObject(this.value);
  
  log() {
    print(value);
  }
}

// 场景三:声明一个类的全局常量
// const用于声明类成员变量时必须同时被声明为static
// 被声明的变量是编译时常量,是类的成员变量
class ConstObject {
  
  const static value = 2;
  
  ConstObject(this.value);
  
  log() {
    print(value);
  }
}

final的使用场景:


// 场景一:文件中的声明一个不可变量
// 必须在声明时赋值,一旦赋值不允许修改,可以省略变量的类型,如var,int等
void main() {
  final c; // 报错,必须声明时赋值
  c = 99  
  
  final c = 99;
  c = 66  // 报错,赋值后不可变
}

// 场景二:类中声明一个不可变量
// 可以在构造函数或初始化列表中初始化,一旦赋值不允许修改,可以省略变量的类型,如var,int等
class TestClass {
  
  final value;
  TestClass(this.value);
  
  // 等价于
  // final value = 2;
  // TestClass();
  
  log() {
    print(value);
  }
}

(3)static

static只能用于声明类的成员变量,它的用法与Java中的static一样。

class TestClass {
  
  // 值是编译时常量,所有类共享该变量(即全局变量)
  static const value = 2;
  
  // 值不是编译时常量,所有类共享该变量(即全局变量)
  static final value;
  
  TestClass(this.value);
  
  log() {
    print(value);
  }
}

3. 函数

 前面说到,Dart是一门真正面向对象的语言,即一切皆对象,包括函数!在Dart中,函数是一等对象,函数可作为顶层函数,类的成员函数,甚至可以被赋值给变量或者作为参数传递给其他函数,其中,当作为顶层函数时其对类的属性和方法可见。【注意:函数返回值没有类型推断,如果函数指定返回值类型,默认当作dynamic处理。

3.1 函数定义

[类型] 函数(类型 参数1, 类型 参数2,...) {  
    return 返回值
}

 其中, 返回类型可以省略,Dart会自动推到返回值类型。如果返回类型为void,return语句可省略。 所有函数都会返回一个值。 如果没有明确指定返回值,函数体会被隐式的添加return null;语句,即函数的返回值为null。

int add(int a, int b) {
    return a+b;
}

// 等价于
add(int a, int b) {
    return a+b;
}

// 等价于
// 当函数体只有一句表达式(expr)时,可以使用=>expr简化
// =>expr语法是{return expr;}的简写
add(int a, int b) => a+b;

3.2 函数参数

 Dart的函数有两种参数类型:requiredoptional。required 类型参数在参数最前面,随后是optional类型参数。

  • required 类型

 required类型参数使用@required注解标识,被标注的参数在函数被调用时必须传入,否则会报错。@required注解通常可以省略,但是当标识命令参数时,不能省略。

String saySomeThing(@required String who, @required String msg) {
    print('Someone is $who, want to say $msg')
}

// 等价于
String saySomeThing(String who, String msg) {
    print('Someone is $who, want to say $msg')
}
  • optional类型

 optional类型分为命名可选参数和位置可选参数,其中,命令可选参数使用{}包裹,位置可选参数使用[]包裹。被标注为可选的参数在函数调用时,可以不传入且不会报错。

 命名可选参数:

void printPersonInf({@require String name, int age=22, String sex}) {
    print('person info = $name, ${age.toString()}, $sex')
}

// 调用
// printPersonInf(name: "jiangdg", age: 18, sex: male);
// 其中,除了name不可省略,其他均可省略

 位置可选参数:

String say(String from, String msg, [String device='HUAWEI']) {
  var result = '$from says $msg';
  if (device != null) {
    result = '$result with a $device';
  }
  return result;
}

// 调用
// 忽略传入device参数,分析器不会报错
// var result = say("jiangdg", "Hello World!");

 注:从上述示例中可知,两种可选参数均支持传入默认值,使用=赋值即可。

3.3 函数的用法

 Dart中函数是一等对象,函数可作为顶层(顶级)函数,类的成员函数,甚至可以被赋值给变量或者作为参数传递给其他函数。

  • a. 函数作为变量

 Dart中的函数时一个Function类型的对象,因此可以被赋值给一个变量:

var message = (str) {
    print(str);
}

// 调用message
message('download success')
  • b. 函数作为另一个函数参数

 函数作为一个对象,因此可以被作为参数传递:

void execute(var callback) {
    callback();
}

// 执行execute
// 传入的函数对象为匿名函数或称Lambda表达式
execute(()=>print(‘Hello World!));
  • c. 匿名函数(闭包,lambda函数)

 所谓匿名函数,就是没有名字的函数。匿名函数,又被称为Lambda函数或Closure(闭包),它和其它函数一样,除了没有名字,也拥有形参列表,可选参数。函数体为:

([[Type] param1[, …]]) {
  codeBlock;
};

 举个例子:

(num x) => x;//没有函数名,有必需的位置参数x
(num x) {return x;}//等价于上面形式
(int x, [int step]) => x + step;//没有函数名,有可选的位置参数step
(int x, {int step1, int step2}) => x + step1 + step2;没有函数名,有可选的命名参数step1、step2

var list = ['apples', 'bananas', 'oranges'];
list.forEach((item) {
  print('${list.indexOf(item)}: $item');
});

// 特别的,如果匿名函数体只有一条语句,可以使用箭头缩写
// 因此这类函数又称为箭头函数
// 上述代码等价于
var list = ['apples', 'bananas', 'oranges'];
list.forEach((item)=>print('${list.indexOf(item)}: $item'));

闭包

 闭包是一个函数对象,它被定义再其它函数内部,并且能够访问外部方法内的局部变量,并持有其状态。通过闭包,可以将其暴露出去,提供给外部访问。上述匿名函数的例子,实际上就是闭包的体现,但为了理解,接下来看下面的例子:

void main() {
    // 获取闭包
    // log()是闭包的外部函数
    val logFunc = log();
    for(var i = 0; i<5; i++) {
        // 执行闭包
        logFunc()
    }
}

// 声明一个无返回值类型,无形参的函数
log() {
    int count = 0;
    
    // 内部声明一个函数,即闭包
    // 将持有外部函数的局部变量count的状态
    printCount() {
        print(count++);
    }
    // 返回闭包
    return printCount;
}

打印结果为:0,1,2,3,4

 由于闭包被定义在一个函数内部,因此又称其为嵌套函数,Dart语言允许多层函数嵌套,而嵌套函数的作用域可为:

// 顶层作用域变量
bool topLevel = true;

void main() {
  // main函数作用域变量
  var insideMain = true;

  void myFunction() {
    // myFunction函数作用域变量
    var insideFunction = true;

    void nestedFunction() {
      // nestedFunction函数作用域变量
      var insideNestedFunction = true;

      // nestedFunction函数中可以访问上面所有层级中的变量。
      print(topLevel);
      print(insideMain);
      print(insideFunction);
      print(insideNestedFunction);
    }
  }
}
  • 顶层函数和main()函数

 Dart中有一种特别,它不需要在类中定义,而是直接基于dart文件顶层定义函数。main函数就是一个顶层函数。任何应用都必须有一个顶级main()函数,作为应用服务的入口。main()函数返回值为空,参数为一个可选的List<String>。示例如下:

//顶层函数,不定义在类的内部
main() {
  print('hello dart');
}

class Number {
    static int getValue() => 100;//static修饰定义在类的内部。
}

 注:static关键字用于声明一个静态函数,且必须定义在类的内部。

参考文献:

1. Dart编程语言中文网

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 黑客帝国 设计师:上身试试 返回首页