美文网首页futter学习
Flutter的敲门砖:Dart语言必备基础知识

Flutter的敲门砖:Dart语言必备基础知识

作者: dvlproad | 来源:发表于2019-03-04 11:39 被阅读4次

前言

如果你只是写界面可能你看Flutter的Widget就够了。

可是当你写业务的时候,你不去了解Dart语言那么你讲寸步难行。

本文致力于以最小的篇幅介绍Flutter的敲门砖:Dart最必备的基础知识。

  • 在Dart中,一切都是对象,一切对象都是class的实例,哪怕是数字类型、方法甚至null都是对象,所有的对象都是继承自Object

  • 跟Java不同的是,Dart没有public protected private等关键字,如果某个变量以下划线(_)开头,代表这个变量在库中是私有的

一、变量

1、变量定义

  int b = 10;
  String s = "hello";
  var a = 1;
  dynamic c = 0.5;

可以明确指定某个变量的类型,如int bool String,也可以用var或 dynamic来声明一个变量,Dart会自动推断其数据类型。

dynamic 则是告诉编译器,我们知道自己在做什么, 不用做类型检测

2、变量类型检测

为了在运行时检测进行类型检测,Dart 提供了一个关键字 is:

dynamic obj = {};
if (obj is Map) {
  // 进过类型判断后,Dart 知道 obj 是一个 Map,所以这里再不用强制转换 obj 的类型为Map。
  obj['foo'] = 42;
}

// 虽然 Dart 也提供了 as 让我们进行类型的强制转换,但为了进来更安全的转换,更推荐使用 is
var map = obj as Map;

二、函数

1、函数的写法

以下三种函数的写法,都是正确的。

// 写法1:声明返回值(推荐)
int add(int a, int b) {
  return a + b;
}

// 写法2:不声明返回值(不加返回值的函数同样可以正常工作)
add2(int a, int b) {
  return a + b;
}

// 写法3:你还可以用=>代替return语句(=>是return语句的简写)
add3(a, b) => a + b; 

main() {
  print(add(1, 2)); // 3
  print(add2(2, 3)); // 5
  print(add3(1, 2)); // 3
}

所有的函数都有返回值,如果没有指定return语句,那么该函数的返回值为null。

2、函数的参数(可选/必选)

void main() {
  print(option_foo1(2));     // 结果为2
  print(option_foo1(1, 2)); // 结果为3
  
  print(require_foo(2));    // 错误:结果为2
  print(require_foo(1, 2)); // 结果为3
  
  // 具名参数的顺序可以是任意的
  print(named_foo1(x: 1, y: 2));
  print(named_foo1(y: 3, x: 4));
  print(named_foo1()); //错误:会导致 foo() 在运行时抛异常
  print(named_foo2(x: 1, y: 2));
  print(named_foo2()); // 正确:不会导致 foo() 在运行时抛异常
}

// 可选参数
// 使用可选参数的函数写法1:含可选参数的函数
int option_foo1(int x, [int y]) {
  if (y == null) { // 是的,int 也可以是 null
    return x;
  } else {
    return x + y;
  }
}
// 使用可选参数的函数写法2:可选参数支持有默认值
int option_foo2(int x, [int y = 0]) {
  return x + y;
}


// 必须参数(如果想告诉用户某个具名参数是必须的,可以使用注解 @require)
int require_foo({@required int x, @required int y}) {
  return x + y;
}

// 具名参数(named parameters)的用法,调用时候具名参数的顺序可以是任意的
// 使用具名参数的函数参数写法1:
int named_foo1({int x, int y}) {
  return x + y;
}
// 使用具名参数的函数参数写法2:所有的具名参数本身就都是可选的,而且具名参数也可以有默认参数
int named_foo2({int x = 0, int y = 0}) {
  return x + y;
}

3、函数做参数/返回值

// 用typedef用于定义函数类型的别名
typedef Adder = int Function(int, int);

Adder makeAdder1(int extra) {
  int adder(int x, int y) {
    return x + y + extra;
  }
  return adder;
}

Dart 里面不仅变量支持类型推断,lambda 的参数也支持自动推断。上面的代码还可以进一步简化为:
Adder makeAdder2(int extra) {
  // 我们要返回的类型是 Adder,所以 Dart 知道 x, y 都是 int
  return (x, y) => x + y + extra;
}

void main() {
  var adder1 = makeAdder1(2);
  print(adder1(1, 2));
    
  var adder2 = makeAdder2(2);
  print(adder2(1, 2));
}

4、函数类型

Dart是一个面向对象的编程语言,所以即使是函数也是一个对象,也有一种类型Function,这就意味着函数可以赋值给某个变量或者作为参数传给另外的函数。

printNum(int a) {
  print("$a");
}

test(Function callback) {
  callback("hello");
}


main() {
  var f1 = printNum;
  Function f2 = printNum;
  var f3 = (int a) => print("a = $a");
  f1(1);
  f2(2);
  f3(6);
}

三、类

1、类的定义

class User {
  // 成员变量写法1:
  String name;
  int age;
  String gender;
  // 成员变量写法2:使用getter/setter声明的成员变量
  String get uid => "24210853532539238";
  set right(String uid) => left = value - width;
  
  // 通过创建一个与其类同名的函数来声明构造函数。如果您未声明构造函数,则会为您提供默认构造函数。默认构造函数没有参数,并在超类中调用无参数构造函数。
  // 构造方法写法1:跟类名相同的构造方法
  User(this.name, this.age, this.gender);
  // 构造方法写法2:命名的构造方法
  User.defaultUser() {
    name = "dvlp";
    age = 20;
  }
  
  // 成员方法写法1:有方法体(是非抽象方法)
  sayHello() {
    print("hello, this is $name, I am $age years old, I am a $gender");
  }
  // 成员方法写法2:没有方法体(是抽象方法,需要子类去实现)
  void doSomething();
}

// 抽象类(无法实例化的类),如果希望抽象类看起来是可实例化的,请定义工厂构造函数。
// 抽象类通常有抽象方法,如下:
// 此类声明为abstract,因此是抽象类,即也就无法实例化
abstract class Test {
  //定义构造函数,字段,方法...
    
  // 抽象方法
  void test();
}

说明类中的构造方法Person(this.name, this.age, this.gender);的这种语法是Dart比较独特而简洁的构造方法声明方式,它等同于下面的代码:

User(String name, int age, String gender) {
    this.name = name;
    this.age = age;
    this.gender = gender;
}

其他声明成员变量的方法:

// 使用getter/setter声明的成员变量
class Rectangle {
  num left, top, width, height;

  // 构造方法传入left, top, width, height几个参数
  Rectangle(this.left, this.top, this.width, this.height);

  // right, bottom两个成员变量提供getter/setter方法
  num get right => left + width;
  set right(num value) => left = value - width;
  num get bottom => top + height;
  set bottom(num value) => top = value - height;
}

2、类的使用

main() {
  var user1 = new Person("zhangsan", 20, "male");
  user.age = 30;
  user.gender = "female";
  user.sayHello();
    
  var user2 = new Person.defaultUser()
}

当我们调用一个不存在的方法时,会执行 noSuchMethod() 方法,默认情况下它会抛出 NoSuchMethodError。你也可以重写noSuchMethod(),改成你输出你希望的错误提示

class Test {
  // 除非你重写noSuchMethod,否则使用不存在的成员会导致NoSuchMethodError
  // Unless you override noSuchMethod, using a
  // non-existent member results in a NoSuchMethodError.
  @override
  void noSuchMethod(Invocation invocation) {
    print('You tried to use a non-existent member: ' +
        '${invocation.memberName}');
  }
}

3、类的继承

Dart中使用extends关键字做类的继承。

class Developer extends User {
  // 实现了父类的抽象方法
  void doSomething() {
    print("I'm developing...");
  }
}

虽然 Dart 是单继承的,但它也提供了一定程度的多重继承支持:

使用@override注解声明你要重写的函数,在这个函数内部可以使用super调用重写的这个父类的函数。

class FlutterDeveloper extends Developer {
  @override // 表示doSomething是重写父类的方法
  void doSomething() {
    super.doSomething();
    print("I'm developing Flutter...");
  }
}

class iOSDeveloper extends Developer {
  @override // 表示doSomething是重写父类的方法
  void doSomething() {
    super.doSomething();
    print("I'm developing iOS...");
  }
}

class FullStackDeveloper extends Developer with iOSDeveloper with FlutterDeveloper {
  @override // 表示doSomething是重写父类的方法
  void doSomething() {
    super.doSomething();
    print("I'm developing...");
  }
}

mixins

mixins是一个重复使用类中代码的方式,比如下面的代码:

class A {
  a() {
    print("A's a()");
  }
}

class B {
  b() {
    print("B's b()");
  }
}

// 使用with关键字,表示类C是由类A和类B混合而构成
class C = A with B;

main() {
  C c = new C();
  c.a(); // A's a()
  c.b(); // B's b()
}

四、库

// 用import语句来导入某个包
// 如果你想导入自己写的某个代码文件,使用相对路径即可
import './util.dart';

// 你可以使用as关键字为导入的某个包设置一个前缀,或者说别名
import 'package:lib2/lib2.dart' as lib2;

// 导入包时使用deferred as可以让这个包懒加载,懒加载的包只会在该包被使用时得到加载,而不是一开始就加载,比如
import 'package:greetings/hello.dart' deferred as hello;

1、问题一:在同一工程中可否有两个同名的类

import 'package:lib1/lib1.dart';
import 'package:lib2/lib2.dart' as lib2;

// Uses Element from lib1.
Element element1 = Element();

// Uses Element from lib2.
lib2.Element element2 = lib2.Element();

2、问题二、如何只允许/只不允许使用某个包中的部分功能

// 只导入lib1中的foo功能
import 'package:lib1/lib1.dart' show foo;

// 导入lib2中的除了foo外的所有其他部分
import 'package:lib2/lib2.dart' hide foo;

五、运算符

1、常见的

运算符 含义
++var var=var+1表达式的值为var+1
var++ var=var+1表达式的值为var
--var var=var-1表达式的值为var-1
var-- var=var-1表达式的值为var

++a

a == b

b ? a : b

2、特殊的

2.1、..运算符(级联操作)

使用..调用某个对象的方法(或者成员变量)时,返回值是这个对象本身,所以你可以接着使用..调用这个对象的其他方法。

class Person {
  eat() {
    print("I am eating...");
  }

  sleep() {
    print("I am sleeping...");
  }

  study() {
    print("I am studying...");
  }
}

main() {
  // 依次打印
  //  I am eating...
  //  I am sleeping...
  //  I am studying...
  new Person()..eat()
      ..sleep()
      ..study();
}

六、控制流程

语句 说明
if / else
switch 略(switch 也支持 String 和 enum)
for /while
try / catch
// switch语句
  String a = "hello";
  // case语句中的数据类型必须是跟switch中的类型一致
  switch (a) {
    case "hello":
      print("haha");
      break;
    case "world":
      print("heihei");
      break;
    default:
      print("WTF");
  }

七、异步

如果一个方法中有耗时的操作,你需要将这个方法设置成async,并给其中的耗时操作加上await关键字,如果这个方法有返回值,你需要将返回值塞到Future中并返回,如下代码所示:

Future<String> checkVersion() async {
  var version = await lookUpVersion();
  return version;
}

八、其他/奇技淫巧

1、静态成员变量和静态成员方法

// 类的静态成员变量和静态成员方法
class Cons {
  static const name = "zhangsan";
  static sayHello() {
    print("hello, this is ${Cons.name}");
  }
}

main() {
  Cons.sayHello(); // hello, this is zhangsan
  print(Cons.name); // zhangsan
}

2、修饰符final和const

final 跟 Java 里的 final 一样,表示一个运行时常量(在程序运行的时候赋值,赋值后值不再改变)。const 表示一个编译时常量,在程序编译的时候它的值就确定了。

修饰符 作用
final 一个被final修饰的变量只能被赋值一次,
const 一个被const修饰的变量是一个编译时常量(const常量毫无疑问也是final常量)

final和const的区别:

区别一:final 要求变量只能初始化一次,并不要求赋的值一定是编译时常量,可以是常量也可以不是。而 const 要求在声明时初始化,并且赋值必需为编译时常量。

区别二:final 是惰性初始化,即在运行时第一次使用前才初始化。而 const 是在编译时就确定值了。

3、泛型:

var names = List<String>();

其他参考文档:

相关文章

网友评论

    本文标题:Flutter的敲门砖:Dart语言必备基础知识

    本文链接:https://www.haomeiwen.com/subject/sdvbuqtx.html