Loading

给Java开发者的Dart入门教程

Jetpack Compose今年就要发布了,挺像Flutter,但是由于是测试版,API会变动,所以还是不敢用它做项目,虽然最后还是会跑到Jetpack上,还是决定先学学Flutter玩玩。

DartFlutter的官方语言,官方网站上有一套交互式教程:Intro to Dart for Java Developers,本篇是这套教程的简易笔记。

DartPad,一个在线执行Dart的环境。

类初探

class Bicycle {
  int cadence;
  int speed;
  int gear;
  Bicycle(this.cadence, this.speed, this.gear);
}

void main() {
  var bike = Bicycle(2,0,1);
  print(bike);
}

从上面的一小段代码中,能看到如下信息:

  • Dart的构造方法支持类似Kotlin的省略写法,这点比Java强太多
  • Dart的new关键字可以省略(在Dart2中)
  • Dart支持类型推断

需要注意的点:

  • 如上的类和属性都是公开的,这是Dart的默认权限,而且Dart中没有publicprotectedprivate这类关键字。

运行结果

Instance of 'Bicycle'

toString

修改我们的类,添加如下代码:

@override
String toString() => 'Bicycle: $speed mph';

从上面的代码中,能看到如下信息

  • Dart的方法支持使用=>简写并省略方法体(在只有一条表达式的时候,官方称为one-line函数,这条表达式的值要符合方法返回值)
  • Dart支持在字符串中通过$variableName引用变量(${expression}同样适用)
  • Dart的字符串可以使用单引号

需要注意

  • Dart的@override注解是小写开头

运行结果

Bicycle: 0 mph

库隐私

修改类:

class Bicycle {
  int cadence;
  int _speed = 0; // 将speed修改为_speed
  int gear;
  Bicycle(this.cadence, this.gear); // 去掉构造器中的speed参数
  int get speed => _speed; // 添加one-line get方法
  @override
  String toString() => 'Bicycle: $_speed mph'; // 这里别忘了改
}

void main() {
  var bike = Bicycle(2,1); // 这里要去掉第二个参数
  print(bike);
}

观察:

  • Dart规定使用开头的下划线来保证变量的库隐私
  • Dart提供了特定的setter和getter格式,同样支持one-line
  • Dart为每个公有变量提供了隐式的setter和getter,除非你想强行只读或只写变量;计算或校验传入的值;或者更新一个别处的值,否则你无需重新定义它们。(就相当于Java中的没有getter和setter的public变量)
  • 在Dart中和Java不同的是,你使用instance.variable = xxx设置值就会调用到setter上,使用instance.variable读取值时,就会调用到getter上,这是很多高级语言支持的特性,如Kotlin

输出:

Bicycle: 0 mph

继续修改代码,在main的输出语句前加:

bike._speed = 10;

输出:

Bicycle: 10 mph

观察:

  • 库隐私对同一个文件不奏效,库隐私的作用域是文件

完善

为类添加刹车和加速方法

class Bicycle {
  int cadence;
  int _speed;
  int gear;
  Bicycle(this.cadence,this._speed, this.gear);
  int get speed => _speed;
  @override
  String toString() => 'Bicycle: $_speed mph';
  void applyBrake(int decrement) => _speed -= decrement;
  
  void speedUp(int increment) => _speed += increment;
}

void main() {
  var bike = Bicycle(2,0,1);
  bike.speedUp(20);
  print(bike);
  bike.applyBrake(10);
  print(bike);
}

观察

  • Dart支持+=-=等运算符
  • Dart的库私有属性也可以通过构造器设置

输出

Bicycle: 20 mph
Bicycle: 10 mph

使用可选参数

Java中经常使用不同数量或类型的参数重载构造器,Dart不支持构造器重载,但是使用了另一种方式代替,就是可选参数。

编写Rectangle类

class Point{
  final int x;
  final int y;
  const Point(this.x,this.y);
}

class Rectangle{
  Point origin;
  int width;
  int height;
  Rectangle({this.origin = const Point(0,0),this.width=0,this.height=0});
  
  @override
  toString() => "Rectangle: (${origin.x},${origin.y}) w: $width, h: $height";
}

void main(){
  var rect = Rectangle();
  print(rect);
}

观察:

  • Dart支持使用{name=value}声明可选参数,并赋予初始值
  • Dart的参数一旦使用const标识,它就是一个编译时常量,使用const修饰的构造方法也必须是const的,并且构造方法中的属性也必须是final或const的
  • Dart的返回值支持省略,并通过实际返回值推断

输出:

Rectangle: (0,0) w: 0, h: 0

修改代码:

main() {
  print(Rectangle(origin: const Point(10, 20), width: 100, height: 200));
  print(Rectangle(origin: const Point(10, 10)));
  print(Rectangle(width: 200));
  print(Rectangle());
}

输出

Rectangle: (10,20) w: 100, h: 200
Rectangle: (10,10) w: 0, h: 0
Rectangle: (0,0) w: 200, h: 0
Rectangle: (0,0) w: 0, h: 0

工厂模式

工厂模式是Java中常用的设计模式,比起直接实例化对象的好处有很多,比如:隐藏实例的细节;可以返回返回值类型的子类;返回一个现存的对象(Single-ton、Pool等)

创建Shape示例

import 'dart:math';

abstract class Shape{
  num get area;
}

class Circle implements Shape{
  final num radius;
  Circle(this.radius);
  num get area => pi * radius * radius;
}

class Square implements Shape{
  final num side;
  Square(this.side);
  num get area => pow(side,2);
}

main(){
  final circle = Circle(2);
  final square = Square(2);
  print(circle.area);
  print(square.area);
}

观察:

输出:

12.566370614359172
4

创建顶级方法

创建一个在文件最外部的工厂方法

Shape shapeFactory(String type,num arg) => type=='circle' ? Circle(arg) : Square(arg);

main(){
  final circle = shapeFactory('circle',2);
  final square = shapeFactory('square',2);
  print(circle.area);
  print(square.area);
}

我比较懒,所以写了一个one-line方法,这个方法如果type是circle的话就创建圆,否则创建方形,虽然节省空间时间,但极度降低可读性,项目中不要这么写。

观察:

  • Dart支持三元运算符
  • Dart中使用==判断字符串的值是否相等

创建工厂构造器

import 'dart:math';

abstract class Shape{
  num get area;
  factory Shape(String type,num arg){
    if (type == 'circle') return Circle(2);
    if (type == 'square') return Square(2);
    throw 'Can\'t create $type.';
  }
}

// ...

main(){
  final circle = Shape('circle',2);
  final square = Shape('square',2);

  print(circle.area);
  print(square.area);
  
  final something = Shape('asdf',1);
}

观察:

  • Dart支持factory关键字去适配工厂模式,但我个人感觉,父类一般不会清晰的知道都有什么子类。。。但是如果知道的时候确实很方便很舒服的写法。
  • Dart支持异常处理,而且你可以通过简单的返回一个字符串去定义自己的异常
  • 当异常发生,DartPad会报告一个Uncaught,如果想知道详情,请使用try-catch

接口

Dart中没有接口,因为每个类都是接口。

class CircleMock implements Circle{
  num area;
  num radius;
  CircleMock(this.area,this.radius);
}
  • area继承了Circle的getter和setter

函数式编程

在Dart中,函数也被看作一个对象,这意味着你可以像传递变量一样传递函数,你还可以将Dart类的实例当作函数调用。

非函数式写法

下面是一段没有使用函数式风格的代码:

String scream(int length) => 'A${"a"*length}h!';

main(){
  final values = [1,2,3,5,10,50];
  for(var length in values){
    print(scream(length));
  }
}

输出:

Aah!
Aaah!
Aaaah!
Aaaaaah!
Aaaaaaaaaaah!
Aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah!

观察:

  • Dart支持字符串乘法
  • Dart支持for in语句

函数式写法

String scream(int length) => 'A${"a" * length}h!';

main() {
  final values = [1, 2, 3, 5, 10, 50];
  values.map(scream).forEach(print);
}

和其他函数式编程语言一样,map使用一个函数将每个元素从一个类型转换成另一种类型,forEach对每个元素执行一个方法,无返回值。

类似的函数式方法还有:fold()where()join()skip()等,并且对于map和set类型,Dart也提供内建的支持。

观察:

  • Dart肯定支持泛型才能实现这些方法

使用其他函数

values.skip(1).take(3).map(scream).forEach(print);

输出

Aaah!
Aaaah!
Aaaaaah!
  • skip(n)跳过前n项
  • take(n)取前n项

所以,经过skip(1)跳过了values中的第一项,使用take(3)values中的第二三四项,也就是[2,3,5],然后再应用map和foreach。

恭喜

你已经知道了Java和Dark的不同之处,当然不是所有,下面可以继续学习:

If you'd like to see Dart technologies in action, try the Flutter codelabs.

Learn more

You can learn much more about Dart with the following articles, resources, and websites.

Articles

Resources

Websites

posted @ 2021-03-08 11:24  yudoge  阅读(686)  评论(0编辑  收藏  举报