Dart基础语法

Flutter —— dart基础语法 Flutter —— dart基础语法(1)1. Flutter 项目创建2. Dart 语法2.1 var、final和const2.2 num2.3 string2.4 list 和 map2.5 ??= 和 ??2.6 方法 和 箭头函数2.7 方法的可选参数2.8 方法作为参数传递2.9 匿名函数2.10 闭包 1. Flutter 项目创建 Flutter配置完之后,就可以在Android studio 创建 Flutter项目啦, 点击Create New Flutter Project之后, 阅读详情

Dart语言的特点:

  • 同时支持JIT(Just In Time,即时编译)和AOT(Ahead Of Time,运行前编译)

JIT在运行时即时编译,可以动态下发和执行代码,开发测试效率高,但是运行速度和执行性能会因为运行时即时编译受到影响。Flutter热重载就是基于此特性实现。

AOT即提前编译,可以生成被直接执行的二进制代码,运行速度快,执行性能高,但是每次执行前都需要重新编译,开发测试效率低。发布时使用AOT,使应用运行速度快、执行性能好。

  •  内存分配和垃圾回收

Dart VM的内存分配策略比较简单,创建对象时只需要在堆上移动指针,内存增长始终是线性的,省去了查找可用内存的过程。

Dart的垃圾回收,则是采用了多生代算法。新生代在回收内存时采用“半空间”机制,触发垃圾回收时,Dart会将当前“半空间”中的“活跃”对象拷贝至备用空间,然后整体释放当前空间的所有内容。回收过程中,Dart只需要操作少量的“活跃”对象,没有引用的大量“死亡对象”则被忽略,这样的回收机制很适合Flutter框架中大量Widget销毁重建的场景。

  • 单线程

Dart是单线程模型天然不存在资源竞争和状态同步的问题。这就意味着,一旦某个函数开始执行,就将执行到这个函数结束,而不会被其他Dart代码打断。

所以,Dart中并没有线程,只有Isolate(隔离区)。Isolate之间不会共享内存,就像几个运行在不同进程中的Worker,通过事件循环(Event Looper)在事件队列(Event Queue)上传递消息通信。

  • 无需单独的声明式布局语言

Dart声明式编程布局易于阅读和可视化,使得flutter并不需要类似JSX或XML的声明式布局语言。

开发过程中也不需要可视化界面构建器,因为热重载可以让我们立即在手机上看到运行效果。

一、数据类型

1.1、Numbers

 

  • 超类num中定义了基本运算符,比如:+、-、*、/ 等,还定义了abs()、ceil()和floor()等方法。

//定义数值型变量n
num n = 1;

//num是int和double的超类,所以可以被赋值为int和double值
n = 3;
n = 3.1415;
  • int:长度不超过64位,具体取值范围依赖于不同的平台。整型还支持位运算操作,比如:移位(<<、>> 和 >>>)、补码 (~)、按位与 (&)、按位或 (|) 以及按位异或 (^)。

//定义int类型变量
int a = 1;
//定义可变类型b,通过字面量推断出b是int类型
var b = 2;

//a和b在定义后,类型已经被固定为int,赋值double会报错:A value of type 'double' can't be //assigned to a variable of type 'int'.
a=3.1415;
  • double:长度为64位。
//定义double类型变量
double c = 3.1415;
//定义可变类型d,通过字面量推断出d是double类型
var d = 3.1415;

//整数字面量将在必要的时候自动转换成浮点字面量(Dart2.1之后)
c = 100;
  • 在dart:math类中定义了更多操作方法,如Random。

注:数据类型在native和web平台有一些差异,具体可参考官网说明, Numbers in Darticon-default.png?t=MBR7https://dart.dev/guides/language/numbers

1.2、Strings

Dart中的字符串是包含了UTF-16编码的字符序列。字符串是不可变的对象,也就是说字符串可以创建但是不能被修改。

  • 可以使用单引号或者双引号来创建字符串字面量
var str1='Hello,world!';
var str1="Hello,world!";
  • 可以使用三个单引号或者三个双引号来创建多行字符串
var s1 = '''
You can create
multi-line strings like this one.
''';

var s2 = """This is also a
multi-line string.""";
  • 字符串插值
//字符串插值
var s = '字符串插值';
assert('Dart 有$s,使用起来非常方便。' == 'Dart 有字符串插值,使用起来非常方便。');
assert('使用${s.substring(3, 5)}表达式也非常方便。' == '使用插值表达式也非常方便。');
  • 原始字符串
var s = r'在 raw 字符串中,转义字符串 \n 会直接输出 “\n” 而不是转义为换行。';
  • 字符串操作
var s1 = 'hello word!';
var s2 = 'hello word!';
//判断两个对象的内容是否一样
assert(s1 == s2); //true
//字符串拼接
var s3 = s1+" "+s2; //hello word! hello word!
//字符串重复
var s4 = s1*2; //hello word!hello word!
//根据下标取字符
var s5 = s1[0]; //h
//字符串长度
s1.length
//是否包含子字符串
s1.contains("o");
//字符串截取
s1.substring(0,2);
//字符串开头
s1.startsWith("he");
//字符串结尾
s1.endsWith("d!");
//字符串为空
s1.isEmpty;
//字符串非空
s1.isNotEmpty;
//字符串拆分
s1.split(' ');
//字符串转小写
s1.toLoWerCase();
//字符串转大写
s1.toUpperCase();
//字符串去除前后空格
s1.trim();
//字符串去除左侧空格
s1.trimLeft();
//字符串去除右侧空格
s1.trimRight();
//字符串字符替换
s1.replaceAll("a","b");
...
  • 字符串和数值之间转换
// 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 【toStringAsFixed是按照四舍五入的规则截取的】
String piAsString = 3.14159.toStringAsFixed(2);
assert(piAsString == '3.14');

1.3、bool

Dart 使用 bool 关键字表示布尔类型,布尔类型只有两个对象 true 和 false,两者都是编译时常量。

bool b=true;
//使用字面量创建变量
var b1 = true;

Dart 的类型安全不允许你使用类似 if (nonbooleanValue) 或者 assert (nonbooleanValue) 这样的代码检查布尔值。

1.4、list

Dart中list表示的更像是Java中的数组,有序并且元素可重复。

  • 字面量创建变量
//这里 Dart 推断出 `list` 的类型为 `List<int>`,如果往该数组中添加一个非 int 类型的对象则会报错。
var list = [1, 2, 3];
  • 指定泛型创建变量
var list = <String>["张三", "王五", "张三", "李四"];
  • 常规操作
// 创建一个空数组 & 数组判空
var fruits = <String>[];
assert(fruits.isEmpty);

// 添加元素
fruits.add('kiwis');
//添加多个元素 & 获取数组长度
fruits.addAll(['grapes','apples', 'bananas']);
assert(fruits.length == 4);

// 获取数组中元素的下标 & 移除元素
var appleIndex = fruits.indexOf('apples');
fruits.removeAt(appleIndex);
assert(fruits.length == 3);

// 清空数组
fruits.clear();
assert(fruits.isEmpty);
  • Dart 在 2.3 引入了 扩展操作符(...)和 空感知扩展操作符(...?),如下:
//你可以使用扩展操作符(...)将一个 List 中的所有元素插入到另一个 List 中
var list = [1, 2, 3];
var list2 = [0, ...list];
assert(list2.length == 4);

//如果扩展操作符右边可能为 null ,你可以使用 null-aware 扩展操作符(...?)来避免产生异常:
var list;
var list2 = [0, ...?list];
assert(list2.length == 1);

1.5、set

在 Dart 中,set与list刚好相反是一个⭐无序的,元素唯一的⭐集合。

  • 字面量创建变量
//与list差别在于,set是使用{}来表示
var halogens = {'fluorine', 'chlorine', 'bromine', 'iodine', 'astatine'};
  • 明确类型创建变量
//指定泛型:
var names = <String>{};

//还可以这样定义set
Set<String> names = {};
  • 常规操作
// 创建set
var ingredients = <String>{};

// 添加多个元素
ingredients.addAll(['gold', 'titanium', 'xenon']);
assert(ingredients.length == 3);

// 添加一个元素重复的元素,set集合没有变化
ingredients.add('gold');
assert(ingredients.length == 3);

// 移除一个元素
ingredients.remove('gold');
assert(ingredients.length == 2);

// 传入列表创建set集合
var atomicNumbers = Set.from([79, 22, 54]);

1.6、map

Map 是用来关联 keys 和 values 的对象,其中键和值都可以是任何类型的对象,每个【键】只能出现一次但是【值】可以重复出现多次。

  • 字面量创建变量
//字面量声明
var gifts = {
  // Key: Value
  'first': 'partridge',
  'second': 'turtledoves',
  'fifth': 'golden rings'
};
  • 明确类型创建变量
var gifts = Map<String, String>();
gifts['first'] = 'partridge';
gifts['second'] = 'turtledoves';
gifts['fifth'] = 'golden rings';
  • 常规操作
//定义
var nobleGases = {54: 'xenon'};
assert(nobleGases[54] == 'xenon');

// 判断某个key是否存在
assert(nobleGases.containsKey(54));

//通过key,移除某个键值对
nobleGases.remove(54);

二、函数

2.1、函数的定义

Dart中函数也是一个对象,其类型为Function。

  • 函数定义和调用
void main() {
  //函数调用
  var result = add(1, 2);
}
///定义函数
int add(int a, int b) {
  return a + b;
}
  • 箭头函数

语法 => 表达式 是 { return 表达式; } 的简写, => 有时也称之为 箭头 函数。

//箭头函数
int add(int a, int b) => a + b;
  • 返回值

所有的函数都有返回值。没有显示返回语句的函数最后一行默认为执行 return null;。

foo() {}
assert(foo() == null);

2.2、函数的参数

函数可以有两种形式的参数:必要参数 和 可选参数。可选参数可以是 命名的 或 位置的。

  • 必要参数

必要参数直接定义在参数列表前面,必要参数在调用函数时是必传的。

// a和b都是必要参数
void fun(int a, int b) {}

//调用
fun(1,2);
  • 命名参数

使用{}将一系列参数包裹起来表示命名参数,定义在参数列表中必要参数的后面。

//c是必要参数,a和b是命名参数
void fun(int c, {int? a, int? b}) {}

//调用
//命名参数可不传或者只传其中一个
fun(3);
fun(3, a: 1);
fun(3, b: 2);
//命名参数传参位置可变
fun(3, a: 1, b: 2);
fun(3, b: 2, a: 1);

可以使用 required 来特殊标识一个命名参数是必须的参数:

//c是必要参数,a和b是命名参数,但调用时必须为b参数传值
void fun(int c, {int? a, required int b}) {}

//调用
//a可以不传值,但是b必须传值
fun(3, b: 2);
fun(3, a: 1, b: 2);
  • 位置参数

使用[]将一系列参数包裹起来表示位置参数,同样需要定义在必要参数的后面。

//c是必要参数,a和b是位置参数
void fun(int c, [int? a, int? b]) {}

//调用
fun(3);
fun(3, 1);
fun(3, 1, 2);
  • 参数默认值

可以用=为函数的命名参数和位置参数定义默认值,默认值必须为编译时常量,没有指定默认值的情况下默认值为null。

void fun(int c, {int a=1, int b=2}) {}

void fun(int c, [int a=1, int b=2]) {}
  • 参数是值传递还是引用传递? ⭐Dart中基本类型参数是值传递,对象参数是引用传递。⭐

2.3、main函数

main()函数作为Dart程序的入口函数,返回值为void并且有一个List<String>类型的可选参数。

2.4、函数是一级对象

一级对象是说它可以像其他对象一样使用,例如:可以被赋值给一个变量,可以作为参数传递给函数,可以作为值被函数返回。

void main() {
  // 1.将函数赋值给一个变量
  var v = foo;
  // 2.将函数作为另一个函数的参数
  test(foo);
  // 3.将函数作为另一个函数的返回值
  var func = getFunc();
  func('kobe');
}
// 1.定义一个函数
void foo(String name) {
  print('传入的值:$name');
}
// 2.将函数作为另外一个函数的参数
void test(Function func) {
  func('Aliu');
}
// 3.将函数作为另一个函数的返回值
Function getFunc() {
  return foo;
}

2.5、匿名函数

没有名字的方法,称之为 匿名函数、 Lambda 表达式 或 Closure 闭包。可以将匿名方法赋值给一个变量然后使用它,比如将该变量添加到集合或从中删除。

//定义匿名函数并复制给变量
var fun = (String name) {
  print(name);
};
var list = ["apple", "banana", "orange"];
//将匿名函数作为参数传给forEach函数,在forEach中被调用。
list.forEach(fun);
//输出结果:
apple
banana
orange

2.6、词法作用域

Dart中变量的作用域:

  • 定义在函数内的变量只能在定义它的函数大括号{}内访问。
  • 定义在文件中的变量(也叫顶层变量),整个文件中都能访问到。
//顶层变量
bool topLevel = true;
void main() {
  //函数内变量,相当于Java中的局部变量或者临时变量。
  var insideMain = true;
  void myFunction() {
    var insideFunction = true;
    assert(topLevel);
    assert(insideMain);
    assert(insideFunction);
  }
}

2.7、词法闭包

闭包 即一个函数对象,即使函数对象的调用在它原始作用域之外,依然能够访问在它词法作用域内的变量。

注:这个貌似就是一个匿名函数,不知道理解的对不对。

//sum返回的是一个函数,函数在sum中定义的
Function sum(int addBy) {
  return (int i) => addBy + i;
}

void main() {
  //add2 = (int i) => 2 + i;
  var add2 = sum(2); 
  //*在main函数中可以调用到sum中定义的函数闭包*
  assert(add2(3) == 5);
}

三、运算符

3.1、算术运算符

运算符描述
+加
–减
-表达式一元负, 反转表达式的符号
*乘
/除
~/除并取整
%取模
++自增
--自减

3.2、关系运算符

运算符描述
==相等
!=不等
>大于
<小于
>=大于等于
<=小于等于

3.3、类型判断运算符

运算符描述
as类型转换(也用作指定类前缀)
is如果对象是指定类型则返回 true
is!如果对象是指定类型则返回 false

3.4、赋值运算符

运算符描述
=给变量赋值
??=当变量为null时,给变量赋值;当变量有值时,保持原有的值

3.5、逻辑运算符

运算符描述
!逻辑非,对表达式结果取反
||逻辑或
&&逻辑与

3.6、按位和移位运算符

运算符描述
&按位与
|按位或
按位异或
~按位取反
<<位左移
>>位右移
>>>无符号右移

3.7、条件表达式

  • ?: 条件 ? 表达式 1 : 表达式 2
    如果条件为 true,执行表达式 1并返回执行结果,否则执行表达式 2 并返回执行结果。

  • ?? 表达式 1 ?? 表达式 2
    如果表达式 1 为非 null 则返回其值,否则执行表达式 2 并返回其值。

3.8、级联运算符

级联运算符(.., ?..)可以让你在同一个对象上连续调用多个对象的变量或方法。

  • ..
class Person{
  String? name;
  int? age;
}
void main() {
  var person = Person()
    ..name = "张三"
    ..age = 20;
}
  • ?..   

注: ?..与..相比添加了空类型检查,会判断左侧对象不为空时才执行。

四、异常

4.1、抛出异常

在Dart中使用throw关键字抛出异常,异常可以是系统定义的异常类型,可以是自定义的异常类型,也可以是任意的对象。

4.2、捕获异常

在Dart中可以使用on或catch来捕获异常,使用on来指定异常类型,使用catch来捕获异常对象,两者可同时使用。

try {
} on OutOfLlamasException {
  // A specific exception
} on Exception catch (e) {
  // Anything else that is an exception
} catch (e) {
  // No specified type, handles all
  
}

catch有两个参数,第一个是异常对象,第二个是栈信息(StackTrace)。

try {
   
} catch (e,s) {
 
}

在catch中使用关键字rethrow 可以将捕获的异常再次抛出。

try {
   
} catch (e) {
   rethrow;
}

4.3、Finally

无论是否抛出异常,finally 语句始终执行,如果没有指定 catch 语句来捕获异常,则异常会在执行完 finally 语句后抛出

try {
   
} catch (e) {
  
}finally{

}

4.4、系统提供的异常类型

Dart提供的异常主要分为Exception和Error两大类,下面枚举一下两类具体包含的异常类型:

  • Exception
子类描述
DeferredLoadException延迟库加载失败异常
FormatException格式处理异常
IOExceptionIO操作异常
IsolateSpawnExceptionIsolate创建异常
NullRejectionException空值异常
OSError操作系统异常
TimeoutException异步超时异常

注:IntegerDivisionByZeroException用UnsupportedError代替

  • Error
子类描述
AbstractClassInstantiationError抽象类实例化错误(Dart2中改为编译时错误)
ArgumentError参数错误
AssertionError断言错误
AsyncError异步错误
ConcurrentModificationError并发修改错误
CyclicInitializationError延迟初始化错误
FallThroughErrorSwitch中最后一个case未break时抛出异常
JsonUnsupportedObjectErrorJson不支持错误
NoSuchMethodError没有找到方法错误
NullThrownError抛出空异常错误
OutOfMemoryError内存溢出错误
RemoteError隔离错误
StackOverflowError堆栈溢出错误
StateError状态错误
TypeError动态类型错误
UnimplementedError未实现错误
UnsupportedError不支持错误

注:原有CastError用TypeError代替。

五、类

Dart是支持基于mixin继承机制的面向对象语言,所有对象都是一个实例。在Dart中,虽然类是单继承,但是因为mixin继承机制,可以实现多继承的效果。

5.1、类成员

类的成员由方法和属性组成,成员由类的实例化对象访问。成员访问符有.和?.两种,其中?.比.多了空对象判断(也可以叫成员条件访问符)。

class Person {
  int? age;
  String? name;
}

void main() {
  var person = Person()
    ..name = "张三"
    ..age = 20;
  // . 
  print("${person.name},${person.age}");
  printString(person);
}

void printString(Person? person){
  // ?.
  print("${person?.name},${person?.age}");
}

5.2、构造函数

  • 默认构造函数

如果没有声明构造函数,会默认生成一个无参构造函数,并且调用父类的无参构造函数。

  • 构造函数不被继承

子类不会继承父类的构造函数。

  • 声明构造函数
class Point {
  double x = 0;
  double y = 0;
  //变量名重复时,使用 `this` 关键字引用类成员变量
  Point(double x, double y) {
    this.x = x;
    this.y = y;
  }
}

class Point {
  double x = 0;
  double y = 0;
  //可以简写成这样
  Point(this.x, this.y);
}
  • 命名式构造函数

因为Dart不支持方法的重载,所以我们不能创建相同名称的构造方法,当需要创建多个构造函数的时候,就可以使用命名式构造函数。使用命名式构造函数好处是可以更清晰的表达你的意图。

class Person {
  String? name;
  int? age;

  Person(this.name, this.age) {}

  // 命名构造方法
  Person.withArguments(String name, int age) {
    this.name = name;
    this.age = age;
  }
}

void main() {
// 创建对象
  var p1 = new Person("李四", 20);
  var p2 = new Person.withArguments('张三', 18);
}
  • 初始化列表

初始化列表用于初始化实例变量,执行在构造函数的函数体之前。格式如下:

import 'dart:math';
class Point {
  final double x;
  final double y;
  final double distance;
  
  /// :属性=表达式,属性=表达式
  Point(double a, double b)
      : x = a,
        y = b,
        distance = sqrt(a * a + b * b) {

    //构造函数的函数体执行的时候,x,y和distance已经完成了初始化
    print("$x  $y  $distance");
  }
  
}

void main() {
  var p = Point(3, 4);
}
  • 重定向构造函数

重定向构造函数用于在一个构造函数中去调用另一个构造函数。

class Person {  
  String name;  
  int age;
  
  Person(this.name, this.age);  
  //重定向构造函数
  Person.fromName(String name) : this(name, 0);
  
}
  • 常量构造函数

将类的所有的属性声明为final,并在类的构造函数前加上const关键字,这样就声明出了一个常量构造函数。常量构造函数可以将类对象实例改为编译时常量。当我们传参相同时,拿到的对象实例是同一个。

class Point {

  final double x, y;
  
  //常量构造函数
  const Point(this.x, this.y);
}

void main() {
  //生成常量对象时,需要添加const关键字
  var p1 = const Point(3, 4);

  //变量带有const关键字的话,创建对象时可以省略const
  const p2 = Point(3, 4);

  //未加const关键字,生成的是非常量对象
  var p3 = Point(3, 4);

  //true
  print(identical(p1, p2));
  //false
  print(identical(p1, p3));
  
}
  • 工厂构造函数

使用factory关键字标识的构造方法是工厂构造方法,工厂构造方法需要我们通过代码主动的返回一个实例对象。


class Logger {
  final String name;

  static final Map<String, Logger> _cache = <String, Logger>{};

  factory Logger(String name) {
    return _cache.putIfAbsent(name, () => Logger._internal(name));
  }
  
  Logger._internal(this.name);

  void log(String msg) {
    if (!mute) print(msg);
  }
}

注: V putIfAbsent(K key,V ifAbsent())方法,如果map中有key对应的值则返回该值,否则调用ifAbsent方法获取新值,并将key与新值存入map,然后返回新值。

工厂构造函数可以用来实现单例模式:


void main() {
  XXUtils.getInstance().printStr("hello world !");
}

class XXUtils {

  //工厂构造函数无法访问this,所以这里要用static
  static XXUtils? _cache;

  //工厂方法构造函数,关键字factory
  factory XXUtils.getInstance() => XXUtils._cache ??= XXUtils._newObject();

  //定义一个命名构造函数用来生产实例(私有化的构造函数)
  XXUtils._newObject();

  //随便定义一个方法
  void printStr(String name){
    print(name);
  }
}

5.3、抽象类

抽象类是被关键字abstract标识的类,抽象类常用于声明接口方法,抽象类正常情况下无法实例化,除非提供工厂构造函数。

abstract class Animal {
  //非抽象方法
  void eat(){
    print("它爱吃什么?");
  }
  //抽象方法
  void run();
}

class Dog extends Animal {
  Dog();
  //实现抽象方法
  @override
  void run() {
    print("小狗跑的很快");
  }
}

void main() {
  var dog=Dog();
  //可以调用到抽象类中的非抽象方法
  dog.eat();//它爱吃什么?
  //调用抽象类中抽象方法的实现
  dog.run();//小狗跑的很快
}

5.4、隐式接口

Dart中每个类都隐式的定义了一个接口,这个接口中包含了所有这个类的实例成员以及这个类所实现的其它接口,使用implements关键字实现一个类的时候,实现的就是其隐式接口。

class A {
  var a = 10;

  void methodA1() {}

  void methodA2() {}
}

///需要实现A中所有的成员
class B implements A {

  @override
  int a;

  B(this.a);

  @override
  void methodA1() {
    // TODO: implement methodA1
  }

  @override
  void methodA2() {
    // TODO: implement methodA2
  }
}

5.5、继承

子类可以复写父类方法,子类可以通过super关键字访问父类,子类继承父类中所有的变量和方法(除构造函数以外)。

class Animal {
  //非抽象方法
  void eat() {
    print("它爱吃什么?");
  }
}

class Dog extends Animal {
  Dog();

  //复写父类方法
  @override
  void eat() {
    //使用super关键字调用父类方法
    super.eat();//它爱吃什么?
    //自己的实现内容
    print("小狗爱吃骨头");
  }
}

void main() {
  var dog = Dog();
  dog.eat();
}

5.6、枚举类

枚举类用于定义一些固定数量的常量值。

//使用enum关键字定义枚举类
enum MyColor { red, blue, yellow }

void main() {
  print(MyColor.red.runtimeType); //MyColor.red
  print(MyColor.red.index); //0
  print(MyColor.red.name); //red
  print(MyColor.red.name.runtimeType); //String
  print(MyColor.red.hashCode); //597084885
}

枚举类型有如下两个限制:

  • 枚举不能成为子类,也不可以mixin,你也不可以实现一个枚举。
  • 不能显式地实例化一个枚举类。

5.7、mixin

使用关键字mixin定义的类是mixin类。mixin类变相实现了类的多重继承。

mixin Runner {
  void run() {
    print('在奔跑');
  }
}

mixin Flyer {
  void fly() {
    print('在飞翔');
  }
}

//通过with关键字使用mixin类
class SuperMain with Runner, Flyer {}

void main() {
  var superMan = SuperMain();
  //可以调用到mixin类中的方法
  superMan.run();//在奔跑
  superMan.fly();//在飞翔
}

与implements相比,mixin方式不需要实现接口中的方法。

六、泛型

泛型从字面的意思理解为一种宽泛的类型,使用<>和类型参数(E、T、S、K 和 V等)替代具体的类型。使用泛型定义的类或者方法可以在使用/调用时才指明具体的类型,大大提高了代码的复用程度。

6.1、泛型类

void main() {
  //实例化时,指定类型为String
  var stringCache=Cache<String>();

  stringCache.setByKey("id", "ID123456");
  stringCache.getByKey("id");

  print(stringCache.runtimeType);//类型是Cache<String>
}

//泛型类
class Cache<T> {
  final map = <String, T>{};

  T? getByKey(String key) {
    return map[key];
  }

  void setByKey(String key, T value) {
    map[key] = value;
  }
}

与 Java 不同的是,Java 中的泛型是类型 擦除 的,这意味着泛型类型会在运行时被移除。在 Java 中你可以判断对象是否为 List 但不可以判断对象是否为 List<String>。而Dart的泛型类型是固化的即便在运行时也会保持类型信息。

  • 限制参数化类型

使用 extends 关键字可以限制泛型参数必须是指定类型的子类。

class View {
  int? width;
  int? height;
}
//View子类
class Image extends View {
  //
}
//View子类
class TextView extends View {
  //
}
//泛型类,使用extends限定类型必须是View的子类
class ViewWrapper<T extends View> {
  //泛型变量
  T? mView;
}
void main() {
  var vw1 = ViewWrapper<Image>();
  var vw2 = ViewWrapper<TextView>();
}

6.2、泛型方法

起初 Dart 只支持在类的声明时指定泛型,现在同样也可以在方法上使用泛型,称之为 泛型方法:

T first<T>(List<T> ts) {
  return ts[0];
}

方法 first<T> 的泛型 T 可以在如下地方使用:

  • 函数的返回值类型 (T)
  • 参数的类型 (List<T>)
  • 局部变量的类型 (T tmp)

七、库和可见性

7.1、可见性

Dart中没有定义与JAVA语言类似的访问控制符。Dart中用有无下划线(_)来替代JAVA中的public 或 private访问控制符。

以下划线(_)开头的成员仅在代码库中可见(相当于private的)

//公共方法,可以被其它文件引用和访问
int getScreenWidth(){
  return 1080;
}
//私有方法,当前文件中可以访问
int _getScreenHeight(){
  return 1920;
}

7.2、库

在Dart中我们可以将每个类视为一个Mini Library。

7.2.1、import

import关键字用来引用一个库文件,格式为:import "<URI>",例如:

//导入math库
import 'dart:math';

void main() {
  print(sqrt(9));//3
}
  • Dart中的库主要分为三种:
类型描述URI格式
Dart内置库系统内置的库dart:xxxxxx
远程库发布到pub.dev的库package:xxxxxx
本地库工程目录下的本地库xxx/xxx/xxx.dart
  • 在开发规范中要求我们导包时遵守以下三个规则:
  1. 要把 “dart:” 导入语句放到其他导入语句之前。
  2. 要把 “package:” 导入语句放到项目相关导入语句之前。
  3. 要按照字母顺序来排序每个部分中的语句。
//优先导入"dart:"语句,并按照字母顺序排序
import 'dart:async';
import 'dart:html';

//然后导入"package:"语句,并按照字母顺序排序
import 'package:bar/bar.dart';
import 'package:foo/foo.dart';

//最后是项目内库的导入语句,并按照字母顺序排序
import 'foo.dart';
import 'foo/foo.dart';
  • 指定前缀 当导入的两个代码库中含有有冲突的标识符时,可以为其中一个库指定前缀。
//导入math库,并指定前缀
import "dart:math" as math;

void main() {
  //不能直接调用sqrt函数,必须用前缀math访问sqrt
  print(math.sqrt(9));//3
}
  • 选择性载入

show只载入库的某些部分,hide筛选掉库的某些部分

//只导入math库中的sqrt函数
import "dart:math" show sqrt;

//导入html库中除了HttpRequest之外的其它内容
import "dart:html" as html hide HttpRequest;

void main() {
  //可以访问的到sqrt方法
  print(sqrt(9));
  //报错,访问不到min方法
  print(min(8,9));
  //报错,HttpRequest未定义
  html.HttpRequest();
}
  • 延迟加载

deferred as关键字可以延迟加载库,一般用于优化启动速度。

//延迟加载
import "dart:math" deferred as math;

void main() {
  executeSqrt(9);
}

void executeSqrt(int num) async {
  //异步等待库加载完成
  await math.loadLibrary();
  //调用库函数
  var result = math.sqrt(num);
  print(result);
}

7.2.2、part

part关键字可以将多个dart合并到一个文件中。

  • 定义log_utils.dart文件
//声明当前文件是app_utils.dart文件的一部分
part of 'app_utils.dart';

//打印信息
void logMessage(String message) {
  print(message);
}
  • 定义device_utils.dart文件
//声明当前文件是app_utils.dart文件的一部分
part of 'app_utils.dart';

///获取屏幕宽度
int getScreenWidth(){
  return 1080;
}
///获取屏幕高度
int getScreenHeight(){
  return 1920;
}
  • 定义app_utils.dart文件
//device_utils和log_utils可以访问到这个库
import 'dart:math';

//合并两部分
part 'device_utils.dart';
part 'log_utils.dart';

① 此时如果导入app_utils.dart文件,那么相当于同时导入了device_utils.dart和log_utils.dart两个文件。

② 在device_utils.dart声明了part of 'app_utils.dart'之后,表明device_utils是app_utils的一部分,此时如果app_utils文件顶部导入了某个库,device_utils是可以访问到这个库的;

注:官方已经不建议使用part,推荐我们使用export来替代。

7.2.3、export

import是将库引入到当前文件使用的话,那么export就是导出库给其他文件使用。

  • 定义device_utils.dart文件
///获取屏幕宽度
int getScreenWidth(){
  return 1080;
}
///获取屏幕高度
int getScreenHeight(){
  return 1920;
}
  • 定义app_utils.dart文件
library app_utils;
//将device_utils文件导出
export 'src/utils/device_utils.dart';
  • 使用
//引入app_utils.dart文件
import 'app_utils.dart' ;

void main() {
  //可以访问到device_utils中的方法
  getScreenWidth();
}

注:规范中要求我们导出(export)语句作为一个单独的部分放到所有导入(import)语句之后。

library app_utils;

import 'dart:math';

//在所有import之后
export 'src/utils/device_utils.dart';
export 'xxx';

7.3、library

  • Library 的代码位于 lib 目录下,且对于其他 Package 是公开的。
  • 你可以根据需要在 lib 下任意创建组织文件结构。
  • 按照惯例,实现代码会放在 lib/src 目录下。
  • lib/src 目录下的代码被认为是私有的。
  • 其他 Package 应该永远不需要导入 src/... 目录下代码。
  • 通过导出 lib/src 目录的文件到一个 lib 目录的文件,实现对 lib/src 目录中 API 的公开。

案例:

主 Library 文件 shelf.dart 在 lib 目录下,通过 shelf.dart 文件导出 lib/src 目录下的若干文件。

library shelf;

export 'src/cascade.dart' show Cascade;
export 'src/handler.dart' show Handler;
export 'src/hijack_exception.dart' show HijackException;
...

注:show 关键字可以指定具体导出哪些内容,防止导出不必要的API。

AXI4-Stream FIFO IP核实战指南——从配置到跨时钟域处理 本文是AXI4-Stream FIFO IP核的实战指南,详细讲解了其作为数据流缓冲核心的作用。文章重点涵盖从Vivado环境下的IP核配置、关键参数(如深度与宽度)设定,到跨时钟域处理的完整流程。深入解析了TVALID/TREADY握手协议及TLAST等信号,并提供了常见问题排查与调试技巧,帮助开发者高效实现FPGA/SoC系统中可靠的数据流缓冲与同步。 阅读详情

相关推荐

零售-公司间关联交易退货STO的实现方式

零售-公司间关联交易采购 STO-退货 集团公司下经常会出现一家公司和另外一家公司的公司间的关联交易,如跨公司销售、总分公司之间采购,分公司之间的采购等业务;通常公司间采购会采取STO的模式来实现(根据业务模式可以将库存转储统一做成STO的模式,公司间STO、同公司工厂间、同工厂库存地点间 的STO SAP都是支持的); 公司间STO 正向A向B采购都很熟悉,公司间退货STO的其实也是比较普遍的业务现象,很多实施着也将其做成反向的STO(B向A采购),而不是退货STO(A向B,item 选择return的标

weixin_45518759的博客 2835

Dart中的垃圾回收机制

1.介绍 Flutter主要使用Dart开发语言,在调试和发布两个版本中,Dart RunTime是始终存在,但两种版本下的构建方式有很大的差异 2.调试和发布版本下的差异 调试版本下 Dart编译到设备,包含三部分: 1.Dart RunTime 2.jit(Android下的实时编译器)/interpreter(IOS下的解析器) 3.调试和分析服务 发布版本下 ...

lizhou828的专栏 1489

告别MATLAB依赖:手把手教你用Python实现GCC-PHAT时延估计(附完整代码与对比测试)

本文详细介绍了如何使用Python实现GCC-PHAT时延估计算法,替代传统的MATLAB依赖。通过完整的代码示例和性能对比测试,展示了Python在信号处理领域的强大能力,特别是在广义互相关和TDOA应用中的高效实现。文章还提供了实时音频处理和嵌入式部署的实用技巧,帮助工程师快速掌握这一关键技术。

weixin_42667269的博客 221

Dart的垃圾回收器

1.介绍Flutter使用Dart作为开发语言和运行时机制,Dart一直保留着运行时机制,无论是在调试模式(debug)还是发布模式(release),但是两种构建方式之间存在很大的差异。在调试模型下,Dart将所有的管道(需要用到的所有配件)全部装载到设备上:运行时,JIT(the just-in-time)编译器/解释器(JIT for Android and interpreter for iOS),调试和性能分析服务。

m0_64301644的博客 327

Zynq7000混合启动方案:QSPI Flash引导与eMMC ext4文件系统部署实战

本文详细介绍了Zynq7000平台的混合启动方案,结合QSPI Flash引导与eMMC ext4文件系统部署,解决嵌入式Linux系统启动可靠性与大容量存储需求。通过硬件连接配置、分区规划、Petalinux工程配置及实战部署步骤,帮助开发者高效实现系统优化,提升启动速度和文件系统性能。

weixin_29190169的博客 447

Seafile搭建以及onlyoffice在线办公

部署seafile docker-compose.yml 如下: 参考此篇文章 version: '2.0' services: db: image: mariadb:10.1 container_name: seafile-mysql environment: - MYSQL_ROOT_PASSWORD=db_dev # mysql root的密码,同时修改 seafile的DB_ROOT_PASSWD与这个一致 - MYSQL_LOG_CONSOLE

dwdcth的专栏 4656

你不必担心Dart的垃圾回收器(译)

f

qq_27909209的博客 475

音视频开发基础(1):模拟视频技术简介

模拟视频技术是指用模拟技术对动态影像进行捕捉、记来、处理、存储、传输、复现的技术.模拟视频技术的发明、发展与模拟电视系统的发展密不可分.下面将通过模拟电视系统来介绍模拟视频技术中的基本概念. 首先,人的视觉系统的特征为电视等視稿系铳的发明、发展提供了理论依据.光线通过角膜、腫孔、晶状体的折射光线.透过玻璃体到达视网展.视网膜上分布着光感受器,光感受器在光线的剌激下...

琅嬛福地 1493

flutter学习-Dart的垃圾回收器

Dart的垃圾回收器 在学习Flutter的过程中,我们知道Widget只是最终渲染对象(RenderObject)的配置文件,它在build的时候频繁的销毁和创建,那么我们就需要考虑频繁的创建和销毁带来的性能问题? 其实大可不必,因为Dart针对Flutter的Widget的创建和销毁做了专门优化,这也是Flutter在众多语言中选择Dart的一个重要因素,下面我们将来了解下Dart的GC(Garbage Collector) Flutter使用Dart作为开发语言和运行时机制,Dart一直保留着运行

yong_19930826的博客 989

Open3D 体素下采样【2026最新版】

体素滤波,博客长期更新,本文最近更新时间为:2026年4月11日。

点云侠的博客 37万+

SKUA-GOCAD入门教程-第七节 点的创建与编辑3

本文介绍了四种从井数据创建点集的方法

江洋大葱的博客 963

Dart【09】垃圾回收

Dart 的垃圾收集器是,由两个阶段组成:年轻空间清除器和并行标记清除收集器。

JackWu blog 739

2️⃣ Coze创建智能体教学(2025年全新版本)

扣子平台提供强大支持,可将智能体轻松集成至各类社交平台,使智能体得以服务更为庞大的用户群体。当下支持集成的平台如下:​1️⃣豆包:作为聚焦 AI 内容创作的平台,非常适合发布充满创意、娱乐性强的智能体。​2️⃣飞书:适合发布用于办公协作的智能体,助力团队提升工作效率。​3️⃣微信:借助公众号或小程序的形式,能够服务海量微信用户。​4️⃣抖音:适合发布与短视频相关的智能体,为用户提供创意与娱乐服务。​

李同学Lino的博客 4577

Dart 语法基础

Dart 支持单行注释、多行注释和文档注释。

luohuayong76的博客 1791

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

Dart 基础-基础语法

Dart 基础-基础语法 由于学习Flutter的时候不可避免的会使用到Dart语言,Dart语言也因此显得格外重要。 一直想总结一篇语法基础作为备忘, 今天终于实现这么目标了。 尽管是个小目标。 这一篇文章总结一点Dart语法基础 开发环境的配置不作为本文的重点进行讨论。 本文是基于的dart sdk 是stable 2.4.0, devel 2.5.0-dev.1.0 由于笔者主要使用的语言是...

samuelnotes的专栏 9538

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

Dart学习之基础语法

原文地址:https://www.jianshu.com/p/3d927a7bf020 承接上一篇我们搭建了Dart环境,这一篇我们将学习Dart的基础语法。 Dart的所有东西都是对象,包括数字,函数等。 它们都继承自Object,默认是都是null(包括数字),所以数字,字符串都可以调用各种方法。...

wk_先森的博客 1891

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Dart 语言基础语法详解

例如: void printInfo(String name, [int age]) {... }。- 匿名函数: (parameters) { // 函数体 }。例如: void printInfo(String name, [int age = 20]) {... }。例如: (int a, int b) => a - b;- 例如: int add(int a, int b) { return a + b;- dynamic :动态类型,可以存储任何类型的值,但可能会在运行时导致类型错误。

m0_57836225的博客 1377

Dart【00】基础语法

Const 变量在编译时就已经固定。Final 变量或类变量在第一次使用时被初始化,懒加载。Const不能定义对象 Final能定义对象//可以省略String这个类型声明保留换行的字符串可以在编译器里换行""";保留换行的字符串可以在编译器里换行''';print(str);

JackWu blog 2011

Dart语法基础

Dart官网: www.dart.dev Windows版Dart下载安装: https://gekorm.com/dart-windows/ Dart语法基础 //1、入口方法: main(){ print('hello dart'); } void main(){ //表示main方法没有返回值 } function setData:void{} //ctrl + / 可...

Sunshine_Dawn的博客 413

01 dart基础语法

dart基础语法讲解

2503_91291623的博客 379

【dart】语法基础

dart语法基础

七十多岁的人了、 460

Dart-基础语法-更新

Dart-基础语法 之前写过一篇基础语法, 后来读刘望舒的微信推文发现这篇总结的也挺全乎, 特别是环境搭建 ,关键字,异常捕获, 类的继承部分 作为学习参考吧, 转载过来,也方便以后查阅。 原文链接: https://mp.weixin.qq.com/s/agbwTf74gZ3YHoEY8MPSrQ 笔者写的文章链接,可以通过一个一个小栗子粘贴执行 体会语法的不同: Dart基础语法 前言 Dar...

samuelnotes的专栏 5574

dart string 转 bool_Flutter基础-Dart基础语法

Flutter基础-Dart基础语法https://www.zhihu.com/video/1251519245034733568来源: https://www.youtube.com/channel/UCW5YeuERMmlnqo4oq8vwUpg作者: The Net NinJa视频看完了,今天内容有点儿多,但是今天的内容是后面学习Flutter的基础。如果把开发Flutter应用比喻成盖楼的...

weixin_39631899的博客 2628
上一篇: Rxjava3 类型转换操作符
下一篇: Android中对包含数字的字符串进行排序(Kotlin语法)
大脸猫6_6
博客等级 码龄5年 53粉丝 43原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值