(一)Flutter 学习之 Dart 变量和类型系统

Flutter系列文章目录导读:

(一)Flutter学习之Dart变量和类型系统
(二)Flutter学习之Dart展开操作符 和 Control Flow Collections
(三)Flutter学习之Dart函数
(四)Flutter学习之Dart操作符、控制流和异常处理
(五)Flutter学习之Dart面向对象
(六)Flutter学习之Dart异步操作详解
(七)Flutter 学习之开发环境搭建
(八)Flutter 和 Native 之间的通信详解
(九)Android 项目集成 Flutter 模块
(十)Flutter FutureBuilder 优雅构建异步UI
更新中…


前言

前些日子在公司尝试着使用 Flutter 开发一些功能。要想写好 Flutter 程序,首先 Dart 语言是基础。

当然,在实际开发中,可能由于时间的关系,不用了解到 Dart 的方方面面才开始 Flutter 的开发,可以边学边用。

为了形成一个完整的体系,我想先系统的讲解下 Dart 语言,然后在开始 Flutter 相关的介绍。

Dart 相对于 Java 来说有更强的变现力,甚至我觉得比 Kotlin 还要有表现力。Dart 不仅可以用来开发客户端,还可以用来开发Web前端和后端开发。

所以呢,我觉得 Dart 还是一门值得学习的语言,下面就开始我们的 Dart 学习之旅吧

本文是 Dart 相关的第一篇,主要介绍 Dart 语言的变量的定义和类型体系

Hello World

按照惯例,学习一门语言都是从 Hello World 开始的。下面我们看下 Dart 的 Hello World 程序:

main(){

  var str = "Hello world!";

  print(str);
  
}
  • 首先我们定义了顶级的 main 函数,这个程序的入口,如果没有返回值可以省略函数的返回类型
  • 然后我们通过 var 关键字定义了一个字符串,通过类型推导,str是一个字符串类型的变量
  • 最后通过系统的 print 函数输出 str 变量

变量的定义

上面我们提到了 Dart 是支持类型推导的,所以在定义变量的时候可以通过 var 关键字来定义

当然也可以显示的指定 变量的类型,如:

// 通过 var 关键字定义变量,省略具体的类型
var str = "Hello world!";

// 显式指定变量类型
String str = "Hello World!"

在 Dart 中万物皆对象,如 null、函数、数字 都是对象

所以我们的定义的变量如果没有给初始值,那么这个变量的默认是就是 null

int lineCount;
assert(lineCount == null);

定义变量的时候,可以使用 final 关键字来修饰,如

final String name = "chiclaim";

name = "johnny"; // Error

final定义的变量,在定义的时候就要初始化,并且只能赋值一次

定义变量的时候还可以使用 const 关键字来修饰:

const name = "chiclaim"

name = "johnny"; // Error

const 关键字修饰变量,说明这个变量是 编译时常量,如数字、字符串字面量都是 编译时常量

constfinal 区别是 final 表示这个变量只能被赋值一次,const 表示该变量不仅只能被赋值一次,并且该变量是一个常量

const 不仅可以修饰变量,还可以修饰变量的值,表示该值不能修改:

main(){

  // 通过 const 关键字修饰foo变量的值
  var foo = const [];
  // 由于 foo 的值被 const 修饰,所以不能修改里面的值
  // 运行时错误,Unsupported operation: Cannot modify an unmodifiable list
  foo[0] = 0;
  // 可以赋值,因为 foo 变量没有被 final 或 const 修饰
  foo = [1,2,3];
  // 由于foo变量被重新赋值,所以可以修改里面的值
  foo[0] = 0;
  
  print(foo);

}

Dart类型系统

Dart 类型系统如下所示:

  • numbers(数字如整型,浮点型等)
  • strings(字符串)
  • booleans (布尔值)
  • lists (集合框架和数组)
  • sets (Set集合)
  • maps (Map集合)
  • runes (表达Unicode字符)
  • symbols (一般用不到)

numbers

不像 C/C++/Java 等语言,在 Dart 表示数字的类型只有两个:int 和 double

  • int :表示没有小数点的整型,最大值不大于64-bit(-2^63 ~ 2^63-1)底层依赖的系统最大值也不同,如果编译后用于JavaScript,则最大值为 -2^53 ~ 2^53-1

  • double 表示双精度的浮点型

int age = 17;

double weight = 65.5;

double waistline = 30; // 相当于 30.0

需要注意的是两个 int 相除 返回一个 double, 例如:

assert(5 / 2 == 2.5); // Result is a double

如果程序需要的是 int 可以使用 ~/ 操作符, 更多的操作符详见 (四)Flutter学习之Dart操作符、控制流和异常处理

字符串和数字类型之间互转:

// 字符串转化成整型
int num = int.parse("2");

// 字符串转化成浮点型
double pi = double.parse("3.14");

// 浮点型转成字符串
String piStr = 3.14.toString();

// 浮点型转成用小数表示的字符串(可以选择保留几位小数)
String piAsStringFix = 3.14159.toStringAsFixed(2);

string 字符串

字符串的创建

一个 Dart 字符串是个 UTF-16 码元(code units)序列,可以使用单引号或双引号来创建一个字符串

// 双引号创建一个字符串字面量
var s1 = 'Single quotes work well for string literals.';
// 单引号创建一个字符串字面量
var s2 = "Double quotes work just as well.";
//  单引号创建字符串字面量(需要转义符)
var s3 = 'It\'s easy to escape the string delimiter.';
var s4 = "It's even easier to use the other delimiter.";

创建原生(raw)字符串,在字符串前面加上前缀 r

// 一般 \n 是换行符,如果我们想把 \n 当做字符串输出时候,也可以使用创建原生字符串的方式
var raw = r'In a raw string, not even \n gets special treatment.';

我们还可以将表达式(${expression})嵌入字符串中:

const name = "chiclaim";
// 将表达式${expression}嵌入字符串中
var info = "My name is ${name}";
// 如果表达式是一个标识符,可以省略花括号
var info2 = "My name is $name";

字符串的拼接:

// 1. 通过 + 号来拼接字符串
var s2 = 'The + operator ' + 'works, as well.';

// 2. 毗邻的字符串字面量
var s1 = 'String '
    'concatenation'
    " works even over line breaks.";

// 3. 通过多行字符串(使用三引号)
var s1 = '''
You can create
multi-line strings like this one.
''';

var s2 = """This is also a
multi-line string.""";

字符串常用的函数

1. 字符串查询相关
  • contains(str) 字符串中是否包含某个字符串
  • startsWith(str) 字符串是否以某个字符串开头
  • endsWith(str) 字符串是否以某个字符串结尾
  • indexOf(str) 字符串在目标字符串中的位置
// Check whether a string contains another string.
assert('Never odd or even'.contains('odd'));

// Does a string start with another string?
assert('Never odd or even'.startsWith('Never'));

// Does a string end with another string?
assert('Never odd or even'.endsWith('even'));

// Find the location of a string inside a string.
assert('Never odd or even'.indexOf('odd') == 6);
2. 字符串提取
  • substring(startIndex,endIndex) 字符串截取
  • split(Pattern) 字符串分割
  • length 字符串长度
  • [index] 通过索引获取字符串中UTF-16编码单元
  • codeUnits 返回字符串的UTF-16编码单元的不可修改的List
// 字符串截取
assert('Never odd or even'.substring(6, 9) == 'odd');

// 使用正则表达式分割字符串,返回字符串集合
var parts = 'structured web apps'.split(' ');

// 通过索引获取字符串的UTF-16编码单元
assert('Never odd or even'[0] == 'N');

// 循环打印字符串中的字符串
for (var char in 'hello'.split('')) {
    print(char);
}

// 获取字符串中的所有UTF-16码元
var codeUnitList ='Never odd or even'.codeUnits.toList();

// N 的 ASCII 编码的十进制就是 78
assert(codeUnitList[0] == 78);
3. 大小写转换
  • toUpperCase 将字符串全部转成大写
  • toLowerCase 将字符串全部转成小写
// 转成大写
assert('structured web apps'.toUpperCase() == 'STRUCTURED WEB APPS');

// 转成小写
assert('STRUCTURED WEB APPS'.toLowerCase() ==  'structured web apps');
4. 字符串的头尾空格的移除和空字符串
  • trim 去除头和尾的空格
  • isEmpty 字符串是否为空
  • isNotEmpty 字符串是否不为空
// 去除头尾的空格
print('  hello  '.trim() == 'hello');

// 字符串是否为空
print(''.isEmpty);

// 空格不是空字符串
print('  '.isNotEmpty);

// 空格trim后,就变成空字符串
print('  '.trim().isNotEmpty);
5. 字符串部分替换
  • replaceAll

字符串是不可变的,替换函数不是修改原字符串,而是会产生一个新的字符串

var greetingTemplate = 'Hello, NAME!';
var greeting =  greetingTemplate.replaceAll(RegExp('NAME'), 'Bob');

// greetingTemplate didn't change.
assert(greeting != greetingTemplate);
6. 字符串构建

通过编程的方式生成一个字符串,可以使用 StringBuffer 来实现,直到调用 StringBuffer.toString() 函数才会创建字符串

var sb = StringBuffer();

// 通过 .. 实现链式调用
sb
  ..write('Use a StringBuffer for ')
  // 写入一个字符串列表,以空格分割
  ..writeAll(['efficient', 'string', 'creation'], ' ')
  ..write('.');

// 创建字符串
var fullString = sb.toString();

assert(fullString ==
    'Use a StringBuffer for efficient string creation.');

Boolean

在 Dart 中通过 bool 关键字来描述布尔,布尔有两个值:true和false。 true 和 false 都是编译器常量

bool isSuccess = true;
bool isFailed = false;

List

List不仅用来表示List集合(有序的数据集合),也用来表示数组

下面来看下如何创建一个List 字面量

var list = [1, 2, 3]; // 类型推导出list是 List<int>

介绍 const 关键字的时候,也用 const 修饰 List 的值 如:

var constantList = const [1, 2, 3];
// constantList[1] = 1; // Error.

Dart2.3 提供了展开操作符(… 和 …?) 让开发者更加方便的将多个值插入到集合中

var list = [1, 2, 3];
// 展开操作符(...) spread operator
var list2 = [0, ...list];


var list;
// 展开操作符(...?) null-aware spread operator
var list2 = [0, ...?list];

Dart2.3 除了提供了展开操作符,还有 collection ifcollection for

// collection if
var nav = [
  'Home',
  'Furniture',
  'Plants',
  if (promoActive) 'Outlet'
];

// collection for
var listOfInts = [1, 2, 3];
var listOfStrings = [
  '#0',
  for (var i in listOfInts) '#$i'
];
assert(listOfStrings[1] == '#1');

collection if/for 在开发Flutter的时候很有用,可以简化很多代码

Set

Set 表示一个无序的集合,下面看下如何创建一个 Set 字面量

// halogens is Set<String>
var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};

创建一个空 Set :

// 通过var和泛型推导出Set类型
var names = <String>{};

// 显式指定是Set类型
Set<String> names = {}; 

需要注意的是下面是创建了一个Map
var names = {}; 

类似地,展开操作符collection if/for 也可以用到 Set 中

Map

Map 是一个 键值对 集合,下面来看下如何创建 Map 字面量:

// gifts is Map<String, String>
var gifts = {
  // Key:    Value
  'first': 'partridge',
  'second': 'turtledoves',
  'fifth': 'golden rings'
};

// nobleGases is Map<int, String>
var nobleGases = {
  2: 'helium',
  10: 'neon',
  18: 'argon',
};

需要注意的时候,Key只能出现一次,否则会被后面的替换掉

通过中括号 [] 来添加、修改、获取Map中的元素:

var gifts = Map();
//  添加元素
gifts['first'] = 'partridge';

var gifts = {'first': 'partridge'};
// 获取通过Key获取Map中的元素
assert(gifts['first'] == 'partridge');

var gifts = {'first': 'partridge'};
// 修改Map中的元素
gifts['fourth'] = 'calling birds';

类似地,展开操作符collection if/for 也可以用到 Set 中

关于 Dart 的 展开操作符 以及 collection if/for 更多内容可以查看:

(二)Flutter学习之Dart展开操作符 和 Control Flow Collections

Rune

在 Dart 中,Rune类型是一个字符串的UTF-32的码点(code points),我们在介绍 String 类型的时候,提到了 code units

那么在介绍 Rune 之前,我们先来看下 code points 和 code units 是什么?

Unicode 编码为世界上所有的字母、数字、符号定义了对应的数字来表示

  • 码点:Unicode是属于编码字符集的范围。它所做的事情就是将我们需要表示的字符表中的每个字符映射成一个数字,这个数字被称为相应字符的码点(code points)
  • 码元:就是码点多少个字节能存储,比如 UTF-8 码元就是 8 bit 也就是 1 byte,UTF-16 码元就是 2 byte

因为 Dart 字符串是 UTF-16 码元的序列,描述 32-bit Unitcode 的字符串需要特殊的语法

通常地, Unicode使用 \uXXXX 描述码点 ,这里的 XXXX 是一个4位十六进制的值,例如描述心形的符号使用 \u2665 表示

如果多于或者少于 4 个十六进制,则需要加上花括号,例如笑的表情使用 \u{1f600}

String 类有几个属性可以提取字符串的 rune 信息 (例如属性 runes),codeUnitAtcodeUnit 属性返回 16 bit的 码元(code units)

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

Object 和 dynamic

在 Dart 所有的类都是继承自 Object,和Java类似。dynamic 和 Object 允许所有的值赋值给它:

dynamic name;
Object email;

main() {
  name = "chiclaim";
  email = "[email protected]";
}

但是他们是两个完全不同的概念。你甚至可以把 dynamic 不要当做一个类型看待,dynamic 顾名思义就是动态的意思,它只是告诉编译器不要做类型检查

dynamic name;
Object email;

main() {
  // 将 String 赋值给 name
  name = "chiclaim";
  // 打印 name 的长度
  print(name.length);
  
  // 将 int 赋值给name
  name = 1;
  
  // 编译器并不会报错(编译时不做类型检查),运行时才会报错
  // NoSuchMethodError: Class 'int' has no instance getter 'length'.
  print(name.length);
  
  email = "[email protected]";
  // 编译器报错,因为 Object 没有 length 属性
  print(email.length);
}

通过上面的代码示例,相信你对 dynamic 和 Object 的区别有了比较清楚的理解

Dart 在官网的最佳实践中建议开发者,如果你想表达意思是任何对象都可以,使用 Object 代替 dynamic

如果你允许类型推导失败,一般推到失败,编译器会默认给dynamic,但是建议显式地写上 dynamic ,因为代码的阅读者不知道你是忘记了写类型还是:

// 建议
dynamic mergeJson(dynamic original, dynamic changes){
}

// 不建议
mergeJson(original, changes){
}

Dart几个重要的概念

  • 万物接对象:null、函数、数字
  • Dart有类型推导功能,定义变量是可以不指定变量类型,如果不想指定类型,可以使用 dynamic 类型
  • Dart支持泛型,如 List<Int>、List<dynamic>
  • Dart支持 top-level 函数,也支持成员函数和静态函数,同时也支持函数的嵌套
  • Dart没有 public、private、protected 关键字来控制访问权限,Dart 通过下划线来控制访问权限,如果以下划线开头,则表示只能在 library 内可见

Reference

关于 Dart 变量和类型系统 就讲到这里, 更多的关于 Android 学习资料可以查看我的GitHub: https://github.com/chiclaim/AndroidAll

https://dart.dev/guides/language/language-tour
https://github.com/acmerfight/insight_python/blob/master/Unicode_and_Character_Sets.md


如果你觉得本文帮助到你,给我个关注和赞呗!

另外,我为 Android 程序员编写了一份:超详细的 Android 程序员所需要的技术栈思维导图

如果有需要可以移步我的 GitHub -> AndroidAll,里面包含了最全的目录和对应知识点链接,帮你扫除 Android 知识点盲区。 由于篇幅原因只展示了 Android 思维导图:
超详细的Android技术栈

发布了161 篇原创文章 · 获赞 1125 · 访问量 125万+

猜你喜欢

转载自blog.csdn.net/johnny901114/article/details/94364411
今日推荐