Dart 语言基础

进击的学霸...大约 10 分钟前端Flutter

Dart 语言的基础学习

Dart 的变量与类型

具有可空类型的未初始化变量的初始值为 null,即使是具有数值类型的变量,初始值也为空,因为Dart 是类型安全的语言,并且所有类型都是对象类型,都继承自顶层类型 Object,因此一切变量的值都是类的实例(即对象),甚至数字、布尔值、函数和 null 也都是继承自 Object 的对象。

可以在声明变量时不初始化变量,但在使用之前需要为其赋值。顶级变量和类变量是延迟初始化的,它们会在第一次被使用时再初始化

空安全

  1. 当你为变量、参数或另一个相关组件指定类型时,可以控制该类型是否允许 null 。要让一个变量可以为空,你可以在类型声明的末尾添加 ?
  2. 必须在使用变量之前对其进行初始化。可空变量是默认初始化为 null 的。 Dart 不会为非可空类型设置初始值,它强制要求你设置初始值。 Dart 不允许你观察未初始化的变量。这可以防止你在接收者类型可以为 nullnull 不支持的相关方法或属性的情况下使用它
  3. 你不能在可空类型的表达式上访问属性或调用方法。同样的例外情况适用于 null 支持的属性或方法,例如 hashCodetoString()

空安全将潜在的 运行时错误 转变为 编辑时 分析错误。当非空变量处于以下任一状态时,空安全会识别该变量:

  • 未使用非空值进行初始化
  • 赋值为 null

变量定义

Dart 中是类型安全的,因此不能像 js 一样使用 var a = 0; if (a) {...} 这样的判断,必须显式的判断 if(a == 0) 这样。

var

创建变量是由编译器来推断类型

var a = '123';

int、double、bool

int 定义一个整型变量、double 定义一个浮点型变量、bool 定义一个布尔变量(只有 true 和 false 两个对象),true 和 false 是编译时常量(在编译时就已确定的值)。

int a = 1;
double b = 1.1;
bool c = true;

Object、String、num

通过类声明变量

Object 定义一个可变类型的变量

Object a = 123;
a = '123';

String 定义一个字符串变量,可以使用模版嵌入表达式,如果表达式是一个标识符可以省略花括号

String name = 'Tom';
String a = 'Hello day ${1 + 1}!';
String b = 'Hello $name!';
  print('Hello World! $a $b');
}

输出> Hello World! Hello day 2! Hello Tom!

通过成对的三个单引号惑三个双引号来定义多行字符串

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

num 声明一个数字类型

num a = 1;
a += 1.1;

late

延迟初始化变量,可以先只声明,到使用前再初始化(使用时如果没有初始化还是会报错的),这样声明 dart 就不会去判断这个变量的是否空值。当一个 late 修饰的变量在声明时就指定了初始化方法,那么内容会在第一次使用变量时运行初始化。

late String description;
// temperature 变量未使用,readThermometer 函数也不会被调用
late String temperature = readThermometer();

void main() {
  description = 'Feijoada!';
  print(description);
}

const、final

如果你不打算更改一个变量,可以使用 finalconst 修饰它,而不是使用 var 或作为类型附加。一个 final 变量只能设置一次,const 变量是编译时常量。(const 常量隐式包含了 final。)

const 关键字不仅仅可用于声明常量,你还可以使用它来创建常量 值(values),以及声明 创建(create) 常量值的构造函数。任何变量都可以拥有常量值。

在表达式一定是常量的上下文中,const 关键字是隐式的,不需要写,也不应该。(文档上这句话挺难理解的,因为对于 js 开发者来说是理所当然的事情,却被着重强调,看了例子才知道要表达的是什么=_=||)

// good
const primaryColors = [
  Color('red', [255, 0, 0]),
  Color('green', [0, 255, 0]),
  Color('blue', [0, 0, 255]),
];
//bad
const primaryColors = const [
  const Color('red', const [255, 0, 0]),
  const Color('green', const [0, 255, 0]),
  const Color('blue', const [0, 0, 255]),
];

如果变量的 没有被 final 或者 const 修饰,即使它以前被 const 修饰,你也可以修改这个变量:

// 指这样的变更
var a = const [];
a = [1,2,3];

虽然 final 定义的对象不能被修改,但对象的字段可以被更改。相比之下,const 定义的对象及其字段不能被更改:它们是 不可变的。额, const 创建的对象必须属性都是 final 的

class Person {
  var name;
  Person(this.name)
}

final person = Person('Tom')
person.name = 'Jerry';
print(person.name);

基本类型

Records

Records(记录)是匿名的、不可变的聚合类型,与其他集合类型(List、Map)一样,它允许您将多个对象捆绑到一个对象中。与其他集合类型不同的是Record (记录)是固定大小的、异构的和类型化的。

单独的值是位置字段,带名字和冒号的是命名字段

var record = ('foo', a: 1, b: 'Tom', 'Hello World!');
print('${record.$1},${record.a},${record.b},${record.$2}');

(String, String, {int a, String b}) record1 = ('foo', a: 1, b: 'Tom', 'Hello World!');
print(record1);

(String, int) getRecord((String, String, {int a, String b}) recordx) {
  return (recordx.$2, recordx.a);
}
print(getRecord(record1));

用在给函数传参和返回值的地方貌似还行,定义类型较为方便一些

Map、List、Set

Map 是一个键值对集合,其中的每个元素都是一个键值对。键和值可以是任何类型的对象,但键必须是唯一的。Dart 的 Map 是基于哈希表实现的,因此访问元素的速度很快。

List 是一个有序的元素集合,可以包含重复的元素。List 在 Dart 中是可变的,意味着你可以添加、删除或更改其中的元素。

Set 是一个无序的唯一元素集合,即集合中的元素不能重复。Set 在 Dart 中是基于哈希表实现的,因此添加、删除和查找元素的速度很快。

var map1 = {'name': 'Tom', 'sex': 'male'};
var map2 = Map<String, int>();
map2['name'] = 1;
map2['age'] = 23;
map2.forEach((k,v) => print('${k}: ${v}')); 
print(map2 is Map); // true

var arr1 = <int>[1, 2, 3];
var arr2 = List<String>.of(['1','2','3']);
arr2.add('999');
arr2.forEach((v) => print('$v '));
print(arr1 is List<int>);

var set = {'apple', 'banana', 'cherry'};
var set1 = Set<String>();
set1.add('123');
set1.add('tome');
print(set1);

类型间的转换

// String -> int
var one = int.parse('1');
// String -> double
var onePointOne = double.parse('1.1');
// int -> String
String oneAsString = 1.toString();
// double -> String
String piAsString = 3.14159.toStringAsFixed(2);

泛型、别名

泛型是一种编程语言特性,它允许你定义函数、类、接口或数据结构,使其能够接受一个或多个类型参数。这些类型参数在定义时不指定具体的类型,而是在使用时指定。泛型提供了一种方式,使得代码可以操作多种数据类型,同时保持类型安全。

别名和 ts 中的 type 类似,不过多介绍了。

函数

函数是一段用来独立地完成某个功能的代码,它也是对象,类型是 Function。像 js 一样支持箭头函数表达式,且可作为另一函数的参数(PS:在 js 里回调很常见,这里为啥要强调这个能力)。

可选命名参数

// 命名参数:非必传参数,非必传有默认值,必传参数,必传参数也可为空值
void enableFlags({bool? bold, bool? hidden = false, required String a, required String? b}) {...}
enableFlags(a: true, b: false);

可选参数

void foo (String? a, int? b, [bool c]) {...}
foo('a', 1);
foo('a', 1, true);

匿名函数

常用于传参中,和 js 也比较像

list.forEach((item) => print(item);)

Dart 是面向对象的语言,每个对象都是一个类的实例,都继承自顶层类型 Object。Dart 中并没有 publicprotectedprivate 这些关键字,我们只要在声明变量与方法时,在前面加上_即可作为 private 方法使用。如果不加_,则默认为 public。不过,_ 的限制范围并不是类访问级别的,而是库访问级别(在 Dart 中, 每一个 Dart 文件就是一个库)。

命名构造函数

class Point {
  num x, y, z;
  Point(this.x, this.y) : z = 0; // 初始化变量z
  Point.bottom(num x) : this(x, 0); // 重定向构造函数
  void printInfo() => print('($x,$y,$z)');
}

var p = Point.bottom(100);
p.printInfo(); // 输出(100,0,0)

复用

在面向对象的编程语言中,将其他类的变量与方法纳入本类中进行复用的方式一般有两种:继承父类和接口实现。Dart 还支持混入(Mixin),关键字使用 with ,通过混入,一个类里可以以非继承的方式使用其他类中的变量与方法。

class Point {
  num x = 0, y = 0;
  void printInfo() => print('($x,$y)');
}

//Vector继承自Point
class Vector extends Point{
  num z = 0;
  
  void printInfo() => print('($x,$y,$z)'); //覆写了printInfo实现
}

//Coordinate是对Point的接口实现
class Coordinate implements Point {
  num x = 0, y = 0; //成员变量需要重新声明
  void printInfo() => print('($x,$y)'); //成员函数需要重新声明实现
}

var xxx = Vector(); 
xxx
  ..x = 1
  ..y = 2
  ..z = 3; //级联运算符,等同于xxx.x=1; xxx.y=2;xxx.z=3;
xxx.printInfo(); //输出(1,2,3)

var yyy = Coordinate();
yyy
  ..x = 1
  ..y = 2; //级联运算符,等同于yyy.x=1; yyy.y=2;
yyy.printInfo(); //输出(1,2)
print (yyy is Point); //true
print(yyy is Coordinate); //true

// 混入
class Coordinate with Point {}
var yyy = Coordinate();
print (yyy is Point); //true
print(yyy is Coordinate); //true

一般来讲,单继承,多实现,混入是多继承

  • 继承是子类需要复用父类的方法实现
  • 实现接口是复用接口的参数,返回值,和方法名,但不复用方法的实现,在Dart中实现抽象类 更像在java中实现用interface修饰的接口
  • 混入是多继承,当被混入的类有多个同名方法时,调用子类的该方法时,会调用with声明的最后一个拥有该方法的类中的该方法,同时混入中的父类不能继承

运算符

Dart 中用于简化处理变量实例缺失(即 null)的情况的几个额外运算符介绍:

  • ?. 运算符:假设 Point 类有 printInfo() 方法,p 是 Point 的一个可能为 null 的实例。那么,p 调用成员方法的安全代码,可以简化为 p?.printInfo() ,表示 p 为 null 的时候跳过,避免抛出异常。(嗯, ts 中也这样用)
  • ??= 运算符:如果 a 为 null,则给 a 赋值 value,否则跳过。这种用默认值兜底的赋值语句在 Dart 中我们可以用 a ??= value 表示。(类似 ts 中的 a || value)
  • ?? 运算符:如果 a 不为 null,返回 a 的值,否则返回 b。在 Java 或者 C++ 中,我们需要通过三元表达式 (a != null)? a : b 来实现这种情况。而在 Dart 中,这类代码可以简化为 a ?? b。(类似 ts 中的 a && value)

覆写

class Vector {
  num x, y;
  Vector(this.x, this.y);
  // 自定义相加运算符,实现向量相加
  Vector operator +(Vector v) =>  Vector(x + v.x, y + v.y);
  // 覆写相等运算符,判断向量相等
  bool operator == (dynamic v) => x == v.x && y == v.y;
}

final x = Vector(3, 3);
final y = Vector(2, 2);
final z = Vector(1, 1);
print(x == (y + z)); //  输出true

评论
  • 按正序
  • 按倒序
  • 按热度