Flutter学习笔记1,Dart基础-变量

476 阅读2分钟

一,定义变量

1,明确声明变量

明确声明变量格式:

变量类型 变量名称 = 赋值;

定义的变量可以对其值进行修改,但是仅限于相同类型的值。

  String name = '字符串';
  int age = 18;
  double height = 1.88;
  print("$name, $age, $height");

2,类型推导变量

类型推导

类型推导变量格式:

var/dynamic/const/final 变量名称 = 赋值;

var

var声明变量,会对针对赋值的类型,自动推导出变量的类型,可以用runtimeType来或取变量的类型。

  var a;
  var b = 3;
  var c = 1.88;
  var d = 'String';
  var e = [1,2,3];

  print(a.runtimeType); // Null
  print(b.runtimeType); // int
  print(c.runtimeType); // double
  print(d.runtimeType); // String
  print(e.runtimeType); // List<int>

var声明的变量赋值后类型就已经确定,不能够在赋值其他类型的值:

  var a = 1;
  a = 1.88; // 报错:A value of type 'double' can't be assigned to a variable of type 'int'.

dynamic

dynamic声明的变量,会对针对赋值的类型,自动推导出变量的类型,并且可以赋值其他类型的值:

  dynamic a = 1;
  print(a.runtimeType); // int
  a = 'abc'; 
  print(a.runtimeType); // String

final&const

final和const都是用于定义常量,定义之后的值不能够修改:

  final a = "Jack";
  a = "Rose"; // 报错

  const b = 3.14;
  b = 123; // 报错

final和const区别

  • final在赋值时,可以动态获取,如赋值一个函数的结果。
  • const在赋值时,赋值的值必须在编译期间就确定下来。
double getPi() {
  return 3.14;
}

main(List<String> args) {
  final pi1 = getPi();
  const pi2 = 3.14;
  const pi3 = getPi(); // 报错
}

二,数据类型

1,数字类型

数值型可以用 num,整数用 int,浮点数用 double,int 和 double 继承自 num。

  int age = 18;
  int hexAge = 0x12;
  print(age);
  print(hexAge);

  double height = 1.88;
  print(height);

num 可以赋值 int 和 double

  num a = 3;
  print(a.runtimeType); // int
  a = 3.14;
  print(a.runtimeType); // double

double 转 int

  double double1 = 1.4;
  double double2 = 1.6;

  int a = double2.toInt(); // 转换成int,相当于floor
  int b = double1.round(); // 转换成int,四舍五入
  int c = double1.ceil(); // 转换成int,向上取整
  int d = double2.floor(); // 转换成int,向下取整

  print("$a ${a.runtimeType}"); // 1, int
  print("$b ${b.runtimeType}"); // 1, int
  print("$c ${c.runtimeType}"); // 2, int
  print("$d ${d.runtimeType}"); // 1, int

int 转 double

  int i = 1;
  double a = i.toDouble();
  print("$a, ${a.runtimeType}"); // 1.0,double

String 转 num

  num a = int.parse('1'); 
  num b = double.parse('3.14');
  // num c = int.parse('3.14'); // 报错,不可以浮点数字符串转int
  num d = double.parse('3');

  print("$a ${a.runtimeType}"); // 1 int
  print("$b ${b.runtimeType}"); // 3.14 double
  // print("$c ${c.runtimeType}");
  print("$d ${d.runtimeType}"); // 3.0 double

num 转 String

  String str1 = 1.toString(); // 1 String
  String str2 = 1.4.toString(); // 1.4 String
  String str3 = 3.1415926.toStringAsFixed(2);  // 3.14 String
  print("$str1 ${str1.runtimeType}");
  print("$str2 ${str2.runtimeType}");
  print("$str3 ${str3.runtimeType}");

2,布尔类型

bool,取值为 true 或 false,dart中不能非0即真,或非空即真。

  var result = 3 > 4;
  print("$result ${result.runtimeType}"); // false bool

3,字符串类型

Dart字符串是UTF-16编码单元的序列,可以使用单引号或双引号创建一个字符串:

  var str1 = 'str1'; // 单引号创建字符串
  var str2 = "str2"; // 双引号创建字符串
  String str3 = "str\n3"; // \n 可以换行
  // 可以用 ''' 创建多行字符串,每一行前面的空格是有效占位的
  String str4 = '''000
  123
  456
  ''';

  print(str1); // str1
  print(str2); // str2
  /**
   * str
   * 3
   */
  print(str3); 
  /**
   * 000
   *   123
   *   456
   */
  print(str4);

字符串拼接:

  var str1 = '123';
  var str2 = 'abc';
  var str3 = str1 + "+" + str2;
  var str4 = "$str1+$str2";
  print(str3); // 123+abc
  print(str4); // 123+abc

4,集合类型

Dart内置了最常用的三种集合类型:List、Set、Map。

1,List

List是一个有序的集合类型,与数组相似,并且支持泛型。

List的定义:

  // 类型推导定义:如果数组中都是一个类型,泛型推导为对应的类型
  var list1 = ['a', 'b', 'c'];

  // 类型推导定义:如果数组中不是一个类型,泛型推导为Object
  var list2 = [1, 'b', 'c'];

  // 指定类型定义,如果不写泛型,默认泛型为dynamic
  List list3 = [1, 2, 3];

  // 指定类型定义,并写明泛型类型
  List<int> list4 = [4, 5, 6];
  
  // 类型推导定义一个空List,泛型默认为dynamic
  var list5 = [];
  print("$list5, ${list5.runtimeType}");

  // 指定类型定义一个空List,泛型默认为dynamic
  List list6 = [];
  print("$list6, ${list6.runtimeType}");

  // 类型推导定义一个空List,指定泛型类型
  var list7 = <int>[];
  print("$list7, ${list7.runtimeType}");

  // 指定类型定义一个空List,指定泛型类型
  List<int> list8 = [];
  print("$list8, ${list8.runtimeType}");

关于泛型需要注意的是,通过var 类型推导创建,如果有初始值并且初始值都是同一类型,泛型类型会自动为该类型。而通过指定类型创建,如果没有显示值明泛型类型,泛型默认为dynamic,无论是否有初始值。

上面Object和dynamic都是可以指定任意类型,区别在于Object类型编译时会进行语法检查,该类型只能够调用Object类型的方法,否则会显示语法错误。dynamic则不会进行语法检查,运行时如果调用不属于该类型的方法,会抛出运行时错误。

List常用的操作:

  List<int> list = [1, 2, 3];

  // 集合长度: 3
  var listLength = list.length;

  // for in 遍历: 1,2,3
  for (int i in list) {
    print(i);
  }

  // forEach 遍历
  list.forEach((element) => print(element));

  // 尾部添加元素: [1, 2, 3] 尾部添加4 [1, 2, 3, 4]
  list.add(4);

  // 插入元素: [1, 2, 3, 4] index为0位置插入0 [0, 1, 2, 3, 4]
  list.insert(0, 0);

  // 移除尾部元素: [0, 1, 2, 3, 4] 移除尾部元素 [0, 1, 2, 3]
  list.removeLast();

  // 移除指定位置元素: [0, 1, 2, 3] 移除index为0位置的元素 [1, 2, 3]
  list.removeAt(0);

  // 移除元素 [1, 2, 3] 移除元素3 [1, 2] 
  list.remove(3);

  // 是否包含元素 [1, 2]包含2,返回true
  bool contains2 =  list.contains(2);

  // 移除指定区间的元素,从start位置开始,移除移除end - start个元素,如果end = start,则不移除任何元素
  // [1, 2] 从index为0开始,移除(1-0)= 1个元素 [2]
  list.removeRange(0, 1);

  // 批量添加元素 [2] 添加 [3,4,5] [2, 3, 4, 5]
  list.addAll([3, 4, 5]);

  // 通过条件移除元素 [2, 3, 4, 5] 移除能被2整除的元素 [3, 5]
  list.removeWhere((element) => element % 2 == 0);

  
  list = [1,2,2,3];

  // 返回指定元素在List中的第一个下标,[1, 2, 2, 3]中第一个2的index 返回 1
  print(list.indexOf(2)); 

  // 返回指定元素在List中的最后一个下标, [1, 2, 2, 3]中最后一个2的index 返回 2
  print(list.lastIndexOf(2)); 

  // 如果不存在返回-1, [1, 2, 2, 3] 不包含 0,返回 -1
  print(list.indexOf(0));

  // 返回满足条件的第一个元素, [1, 2, 2, 3] 返回第一个不被2整除的元素下标,返回0
  print(list.indexWhere((element) => element % 2 != 0));

  // 返回满足条件的最后一个元素,[1, 2, 2, 3] 返回最后一个不被2整除的元素下标,返回3
  print(list.lastIndexWhere((element) => element % 2 != 0));

部分函数调用的参数使用了匿名函数,比如:list.indexWhere((element) => element % 2 != 0),这里传入的参数是一个函数,具体语法这里不展开,后面关于函数的笔记详细记录。

2,Set

Set类似于其他语言的集合类,它是无序并且不重复的集合,和List一样支持泛型。

Set的定义

  // 类型推导定义 如果初始值都是同一类型,泛型为该类型 int
  // 如果初始值有重复元素,会自动去除重复元素
  var set1 = {1, 2, 2, 3};
  print("$set1 Type: ${set1.runtimeType}");

  // 类型推导定义 如果初始值类型不同,泛型为 Object
  var set2 = {1, 2, '3'};
  print("$set2 Type: ${set2.runtimeType}");

  // 指定类型定义,如果没有写明泛型类型,默认为 dynamic
  Set set3 = {1, 2, 3};
  print("$set3 Type: ${set3.runtimeType}");

  // 指定类型定义,并且指明泛型类型
  Set<int> set4 = {1, 2, 3};
  print("$set4 Type: ${set4.runtimeType}");

  // 类型推导方式想定义一个空元素的Set,需要指定一个泛型,直接 {} 默认初始化类型为Map<dynamic, dynamic>
  var set5 = <int>{};
  print("$set5 Type: ${set5.runtimeType}");

  // 指定类型初始化一个空元素的Set,泛型默认为dynamic
  Set set6 = {};
  print("$set6 Type: ${set6.runtimeType}");
  
  // 指定类型初始化一个空元素的Set,同时指定泛型类型
  Set<int> set7 = {};
  print("$set7 Type: ${set7.runtimeType}");

关于泛型需要注意的是,通过var 类型推导创建,如果有初始值并且初始值都是同一类型,泛型类型会自动为该类型。而通过指定类型创建,如果没有显示值明泛型类型,泛型默认为dynamic,无论是否有初始值。

如果打印Set的runtimeType会发现,类型并不是Set类型:

  Set<int> set1 = {1,2,3};
  print(set1.runtimeType); // _CompactLinkedHashSet<int>

这是因为Set是一个抽象类,其内部通过工厂方法调用其子类LinkedHashSet的初始化方法,LinkedHashSet也是一个抽象类,调用其工厂方法,返回实际的对象,它的工厂方法通过external关键字在其他的地方实现,这里就先不展开,后面会在对象初始化的笔记中详细记录。

// Set
abstract class Set<E> extends EfficientLengthIterable<E> {

factory Set() = LinkedHashSet<E>;
...

// LinkedHashSet
abstract class LinkedHashSet<E> implements Set<E> {

  external factory LinkedHashSet(
      {bool equals(E e1, E e2),
      int hashCode(E e),
      bool isValidKey(potentialKey)});

...

Set常用操作这里也不记录了,大概的语法和和List很多都一样,只是多了些集合之间的交集并集等。

3,Map

Map通过Key和Value存储数据,和其他语言的Map或Objective-C的字典非常相近,本质原理上是一个哈希表。

关于Map的初始化、常用方法其它语言非常类型,并且和Set\List的函数类型基本一致,这里不重复记录。