Dart对比java快速入门

[TOC]

定位

首先本文是通过对比其他语言(主要是java)快速入门,建议您至少要熟悉一门静态语言或者动态语言。Dart和java以及C#都差不多,基本上不用学习可以直接使用,从这里可以你可以了解Dart有些特别之处。

首先从一段代码入门吧:

void main() {
  for (int i = 0; i < 5; i++) {
    printTest('hello ${i + 1}');
  }
}

void printTest(String variableInteger) {
  print(variableInteger);
}

从上面可以看到,Dart语言主入口是main函数,他可以不用属于某个类。一眼看上去和java差不多。流程控制也是由小括号和大括号构成,不用考虑缩进。结尾必须要加上分号

声明数据类型

void main() {
  int l = 4;
  String str1 = "str1";
  var i = 1;
    dynamic d = 0;
  final j = 2;
  const k = 3;
}

Dart是强类型语言,变量都会有一个类型。你可以向java那样直接声明类型,同样可以像swift或者Kotlin那样使用类型推导。Dart的类型推导使用final,var,const,dynamic关键字。

  • var修饰变量,这个没什么好说的。
  • final表示不可变的,修饰内置数据类型,值不可变;修饰对象表示引用不可变,使用到的频率很高。
  • const是编译时常量,他表示始终不可变,无论修饰内置类型还是对象,或者是数据结构。
  • dynamic是任意类型,有点像java里面的Object,Kotlin中的Any

使用const修饰的都是编译时常量。它区别于运行时,它的值是不可变的。
所有的数据类型,不管是int还是String,初始值都是null

数据类型

numbers、strings和booleans

首先是numbers,strings,booleans其中numbers包括intdouble类型,分别表示整数和浮点数。strings就是String类型,普通的字符串。booleans类型是bool只包含truefalse

numbers
* int
* double

strings
* String

booleans
* bool

如下代码:

void main() {
  // numbers
  int i = 1;
  double d = 1.1;
  
  // 字符串转数字
  // 这里就使用了类型推导
  final p1 = "3";
  final p2 = "3.3";
  print(int.parse(p1));
  print(double.parse(p2));
  
  // string
  // 可以使用('xxx')("xxx")('''xxx''') 三种方式初始化字符串,和Kotlin很像。
  // 字符串比较的是值
  final str1 = "test";
  final str2 = "test";
  print(str1 == str2); // true
  
  final str3 = 'test';
  print(str3);
  
  final str4 = '''test''';
  print(str4);
  print(str4 == str3); // true
  
  print("I'm ${str4}!!!");
  
  //booleans
  // null 可以直接用来判断,它是false
  if(null) {
    print('null is true');
  } else {
    print('null is false');
  }
  
  print(''.isEmpty);
//   final list = [];
//   if(!list) {
//     print('not empty');
//   } 
}

Dart的数据结构包括list和map

list

list对应的就是java里面的List,list可以像Python等语言那样使用[]进行数组操作,参照以下示例代码。
List有几个在flutter非常常用的初始化方式,分别为filledgenerate。具体参考:https://api.dartlang.org/stable/2.1.0/dart-core/List-class.html

void main() {
  // 初始化
  final list1 = [1,2,3,4,5];
  // 编译时常量
  final list2 = const [2,3,4,5,6];
//   list2[2] = 8; error
  print(list2);
  
  // 初始化2
  List list3 = new List(5);
  print(list3); // [null, null, null, null, null]
  
  // 初始化3 filled 第一个参数是个数,第二个是添加的值
  List list4 = List.filled(3,10);
  print(list4); // [10,10,10]
  
  // 初始化4 类似Python的Map生成 
  List list5 = List.generate(3,(i) => i * 3);
  print(list5); // [0, 3, 6]
  
  // 更改 使用list1
  list1[2] = 8;
  print(list1); // [1, 2, 8, 4, 5]
  // 添加
  list1.add(10);
//   list1[10] = 20; Index out of range
  print(list1); // [1, 2, 8, 4, 5, 10]
  //删除
  list1.remove(10);
  print(list1); // [1, 2, 8, 4, 5]
}

map

map 对应java里面的Map,他的操作方式有点像Python。需要注意的有几点。

  • Map的Key不能有重复的
  • 如果使用int类型作为key,取值的时候优先使用key匹配,然后是下标。
  • 取不到值不会抛异常,直接返回null
  • Map和List可以转换为Json,来回转换时深拷贝。

其他API参考:https://api.dartlang.org/stable/2.1.0/dart-core/Map-class.html

import 'dart:convert';
void main() {
    final map1 = {
    "test1key" : "test1value",
//     "test1key" : "test1value", Key 不能相同
    123 : "123value",
    null : 'NULL',
    2 : '2'
  };
  print(map1); // {test1key: test1value, 123: 123value, null: NULL, 2: 2}
  // 获取值
  print(map1[123]); // key取值 123value
  print(map1[1]); // 下标取值 null
  print(map1[2]); // 同时存在时候,优先Key取值;结果为 2
  // 追加值
  map1['appendKey']  = 'appendVal';
  print(map1);
  // 更改值
  map1['appendKey']  = 'appendVal123';
  print(map1);
    map1['appendKey'] = null;
  print(map1); // 置为null之后,key不会消失
  map1.remove('appendKey');
  // 删除
  print(map1); 
  
  print(map1['non-key']); // 不会报错 打印 null
  print(map1.length); // 长度 4
  
  // json 转换
  // 使用const修饰的数据结构,不可改变。
  final map2 = const {
    "name":"lecon",
    "age":23,
    "ss":[1,2,3,4,4]
  };
  final jsonText = jsonEncode(map2);
  print(jsonText); // {"name":"lecon","age":23,"ss":[1,2,3,4,4]}
  final mapObj = jsonDecode(jsonText);
  print(mapObj); // {name: lecon, age: 23, ss: [1, 2, 3, 4, 4]}
}

Runes

这东西其实就是字符集的扩编,可以用它显示一些表情符号

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

函数

Dart的函数比java多了几个概念。比如在参数列表里面添加个大括号,是可选命名参数;加个中括号叫可选位置参数

  • 可选命名参数类似Python里面的**kwargs,参考下面示例代码func3
  • 可选位置参数类似Python里面的*args,参考下面func5
  • 可选命名参数和可选位置参数可以有默认值,普通参数不能有,参考func6
  • 函数之间可以互相嵌套,互相调用,但他们都有自己作用域。
  • 若果函数简单可以使用()=> value简写。

比如

String test() {
    return "123";
}

简写成

String test ()=> "123";

下面是示例代码

// import 'package:meta/meta.dart'

void main() {
 func1(123);
 func2('222');
  func3("lecon");
//   func3("lecon","spawn",123); error,使用下边的中括号,注意区别。这个使用key区分,下面使用位置区分
  func3("lecon",param2:"spawn",param3:123);
//   func4("lecon",param2:"spawn"); error required 
  func5("lecon","spawn","hahaa");
  func6("lecon",age:25);
  print(func7());
}

void func1(param1) {
  print(param1);
}
void func2(String param2) {
  print(param2);
}

void func3(String param1,{String param2,int param3}) {
  print("I am ${param1}, Hi ${param2},I am ${param3}");
}

// test in flutter
// void func4(String param1,{String param2,@required int param3}) {
//   print("I am ${param1}, Hi ${param2},I am ${param3}");
// }


void func5(String param1,[String param2,String param3]) {
  print("I am ${param1}, bian bu ${param2} xia qu le ${param3}");
}

// 只有使用{}和[]的才可以有默认值
void func6(String param1,{int age = 23}) {
  print("I am ${param1}, I am ${age}");
}

func7() {
  void func8() {
    
    }
  
  void func9() {
    
  }
  
  retrun () => "123";
}

流程控制

Dart的流程控制和java的用法基本一样,这里就不介绍了。

有个for in要说下,和JavaScript很像。

for (var x in collection) {}

相当于java的高级for循环

for(Integer i : indexs) {}

面向对象

相对于java有这几点需要注意

  • new关键字可以省略
  • 一个类同名构造器只能有一个,也就是说可以给构造器起其他名字。所以可以把构造器分为默认构造器命名构造器
  • 命名构造器不能自动继承,参考下面
  • Dart使用this代表当前对象
  • 初始化列表
  • Dart有继承和接口概念,和java基本差不多,同时多了个混入的的概念minix,这个非常好理解。

下面这个例子是类构造器的基本使用

普通构造器和继承

class Person {
  String name;
  int age;
  String dantengt;
 
  // 注意,冒号后边是初始化列表,用,隔开。
  Person() :dantengt = "danm";
  
  @override
  String toString() => "name : ${name};age : ${age}";
}

class Person2 {
  String name;
  int age;
  
  // 默认构造器只能声明一个
  // 不用写set方法,使用this可以直接把值赋给相应的属性
  Person2(this.name,this.age);
  
  // 命名构造器
  Person2.copy(Person p) {
    this.name  = p.name;
    this.age = p.age;
  }
  
  @override
  String toString() => "name : ${name};age : ${age}";
}

class Student extends Person2 {
  final String school;
  // 普通构造器继承
  Student(String name,int age,this.school):super(name,age);
  // 命名构造器的继承
  Student.copy(Person p,this.school):super.copy(p);
    // 类似构造器的重载
  Student.mySchoolCopy(Person p):this.copy(p,'mySchool');
}

// 所有值都不可以改变
class ImmutablePoint {
  static final ImmutablePoint origin =
      const ImmutablePoint(0, 0);

  final num x, y;
    // cosnt修饰的构造器是常量构造器,里面的值都不许改变。因为是编译时的。
  const ImmutablePoint(this.x, this.y);
}

void main() {
  final person = Person();
  person.name = "spawn";
  person.age = 18;
  print(person);
  
  final person2 = Person2("lecon",25);
  print(person2);
  print(Person2.copy(person));
  
  Person2 p2 = Student("lecon",25,"haha");
  
  // is 和 as ;类似 instanceof和强转
  if (p2 is Student) {
    (p2 as Student).school;
  }
}

其中:

  • Person类的构造器后面有个,那个就是初始化列表,可以给属性赋值(参考Person类),还可调用父类构造器。
  • Person2 类有两个构造器,默认构造器没有名字,和java中一样使用类名声明。另外还有一个Person2.copy() 他也是一个构造器,只不过它有名字了,同名构造器只能出现一次,不管是不是重载(貌似Dart没重载)。
  • 在构造器参数列表中,如果直接使用this关键字,可以直接把值付给类的属性,省去了set方法(参考类Person2的默认构造器)。
  • 在类中,所有的属性或者方法,只要带了_前缀,那么他就是私有的,Dart文件之外不能访问,当前文件不同类是可以的。
  • is关键字相当于instanceofas相当于强转。这个和Kotlin很像。
  • const修饰构造器,代表类是编译时的。所有内容不允许改变。
  • 类的继承使用extends关键字,同时dart也有接口,和java基本一致,不再赘述。
  • 除了继承和接口之外,Dart还有个mixin混入的概念,可以把他理解为:把其他类的东西,一股脑放到当前类中,使用with关键字描述。

minix

关于minix参考以下代码:

// class关键字换成minix,其他和class一样,只是换个名字。
mixin Play {
  void play() {
    print("I can play");
  }
}

mixin Eat {
  void eat() {
    print("I can eat");
  }
}

// 使用with关键字加入mixin类
class Person with Play,Eat {
  final String name;
  final int age;
  Person(this.name,this.age);
}

void main() {
  final p = Person("lecon",13);
  // 此时p有了Eat和Play的能力
  p.play();
  p.eat();
}

factory构造器和多态

java编程中,在使用多态的时候,我们可能会用到工厂方法,dart给我们提供一个方便,使用dart的工厂构造器。dart的工厂构造器也是个构造器,只不过使用factory修饰,他返回当前类,并且不能使用this关键字,示例代码。

class Person2 {
  String name;
  int age;
  
  Person2(this.name,this.age);
  
  // factory 构造器也是构造器,同样不能声明两个相同名字的,而且只能有一个默认
  // factory 没有this引用
  factory Person2.select(name,int age,int type) {
    if(type == 0) {
      return Student(name,age,"jaja");
    } else {
      return Worker(name,age,"lala");
    }
  }
  // callable
  call(String a, String b, String c) => '$a $b $c!';
  
  @override
  String toString() => "name : ${name};age : ${age}";
}

class Student extends Person2 {
  final String school;
  
  Student(String name,int age,this.school):super(name,age);
}

class Worker extends Person2 {
  final String company;
  
  Worker(String name,int age,this.company):super(name,age);
}

void main() { 
    // 不关注Person2实现类,直接使用Person2实例化。使用factory
  final p1 = Person2.select("lecon",23,0);
  final p2 = Person2.select("lecon",23,1);
  print(p1.runtimeType); // Student
  print(p2.runtimeType); // Worker
  // callable
  print(p1("123","234","345"));

}

其中select为Person2的工厂构造器,由它来返回对应的实现类。

setter和getter

很多语言里面,为了方便获取属性都提供了setter和getter。其实他就是个语法糖。直接使用就可以。看例子:

class Rectangle {
  num left, top, width, height;

  Rectangle(this.left, this.top, this.width, this.height);

  num get right => left + width;
  set right(num value) => left = value - width;
  num get bottom => top + height;
  set bottom(num value) => top = value - height;
}

void main() {
  var rect = Rectangle(3, 4, 20, 15);
  assert(rect.left == 3);
  rect.right = 12;
  assert(rect.left == -8);
}

异常

Dart的异常也和java非常像。直接看例子

void main() {
//    throw1();
//    throw2();
//    throw3();
//    throw4();
      throw5();
}

void throw1() {
  throw FormatException('Expected at least 1 section');
}

void throw2() {
  throw 'Expected at least 1 section';
}

void throw3() {
  try {
    throw FormatException('Expected at least 1 section');
  } on FormatException {
    print('section exception');
  }
  try {
    throw FormatException('Expected at least 1 section');
  } on FormatException catch(e) {
     print('section exception ${e}');
  }
  try {
    throw FormatException('Expected at least 1 section');
  } on FormatException catch(e,s) {
    print('section exception ${e}');
    print(s); // 打印方法调用栈
  }
}

void throw4() {
  try {
    throw FormatException('Expected at least 1 section');
  } on FormatException {
    print('section exception');
    rethrow; // 重抛
  }
}

void throw5() {
   try {
    throw FormatException('Expected at least 1 section');
  } on FormatException {
    print('section exception');
  } finally {
     print("I am finally");
  }
}
  • dart可以直接抛出默认异常,throw 'Expected at least 1 section';
  • 使用on来匹配异常类型,on FormatException
  • 使用catch来获取异常变量,catch(e,s)它有一个默认参数和可选参数。分别为信息和调用栈。
  • 使用rethrow重新抛出异常

异步

dart的异步和JavaScript ES6差不多,使用awaitasync,只不过ES6中的Promise变成了Future。目前比较流行的解决方案都是这个,比如Python,Kotlin都这么使用。一般在网络请求或者数据库操作时候使用,就像java的多线程基础版吧。

异步这块东西有点多,而且很重要,直接贴官方地址。https://www.dartlang.org/guides/libraries/library-tour#future

异步中有两个很重要的接口:FutureStream。贴出地址,这两个一定要弄明白。https://www.dartlang.org/guides/libraries/library-tour#dartasync---asynchronous-programming

生成器

Dart的生成成器和其他语言里面的生成器差不多,分为同步异步两种。同步生成器结构是Iterable,异步生成器接口是Stream。流程控住使用async*(异步)和sync*(同步)声明,使用yield流程控制,每一次yield都会给列表生成一个值,也就是说生成器返回结果的数据结构可以当列表使用。以下代码:

void main() {
  print(naturalsTo(10)); // (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
  print(naturalsDownFrom(10)); // (10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
  asynchronousNaturalsTo(10)
    .toList() // toList把异步转为同步
    .then((res) {
      print(res); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    });
}

// 同步生成器
Iterable naturalsTo(int n) sync* {
  int k = 0;
  while (k < n) yield k++;
}

// 异步生成器
Stream asynchronousNaturalsTo(int n) async* {
  int k = 0;
  while (k < n) yield k++;
}

// 递归性质的生成器
Iterable naturalsDownFrom(int n) sync* {
  if (n > 0) {
    yield n;
    yield* naturalsDownFrom(n - 1);
  }
}

其他

import关键字

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

// 仅导入foo
import 'package:lib1/lib1.dart' show foo;

// 排除foo
import 'package:lib2/lib2.dart' hide foo;

// 异步导入
import 'package:greetings/hello.dart' deferred as hello;

空判断

Dart也有null类型,并且和其他语言类似,Dart也是用来解决空问题。

void main() {
  final a = null;
  print(a != null ? a : "b"); // 等同于下边,dart也支持三元运算符
  print(a ?? "b"); // 打印 b
  
  String p = null;
  print(p?.toString()); //打印null,而不会空指针
}

..运算符

Dart的联级运算符..很有意思,很实用的语法糖,直接看代码你就明白了。Builder大法好。

void main() {
   // 联级运算符
    final p = Person()
    ..username = "lecon"
    ..password = "123456"
    ..setSex(true);
  print(p); // Username: lecon; Password: 123456; Sex: true
}

class Person {
  String _username;
  String _password;
  bool _sex;
  
  set username(String username)  => this._username = username;
  
  set password(String password)  => this._password = password;
  
  void setSex(bool sex)  => this._sex = sex;
  
  @override
  String toString() => "Username: ${this._username}; Password: ${this._password}; Sex: ${this._sex}";
}

Typedefs

这个和C++里边的内联函数基本差不多。
官方例子 https://www.dartlang.org/guides/language/language-tour#typedefs

注释

Dart注释分为三种,单行注释和多行注释和java一致。doc注释使用 ///表示。

/// A domesticated South American camelid (Lama glama).
///
/// Andean cultures have used llamas as meat and pack
/// animals since pre-Hispanic times.

最后

临时总结,难免有错误,请多多指出,我会在第一时间改正。

代码地址

https://gist.github.com/leconio/73bd75eef9530ff76ac59e09b1331865

你可能感兴趣的:(Dart对比java快速入门)