Flutter 知识梳理 (Dart) - Dart 和 Java 有哪些不同?

在学习Dart的时候,会遇到一些Java中没有的概念或者用法,这篇文章总结了DartJava中一些不同,但又经常会用到的知识点。

一、构造函数初始化列表

初始化列表定义在构造函数){之间,初始化列表的语句之间用,分割,它的作用有如下几点。

1.1 为final变量赋值

我们希望类中的final变量初始值能由外部传入,但是在构造函数中为final变量赋值是不允许的,例如下面这样:

class ConstField {
  
  final value;
  
  ConstField(int y) {
    value = y;
  }
  
}

此时可以采用Dart提供的初始化列表的方式:

class ConstField {
  
  final value;
  
  ConstField(int input) : value = input;
  
}

只有初始化列表,没有构造函数体的时候,初始化列表最后用;结尾。

1.2 对输入参数进行判断

class ConstField {
  
  final value;
  
  ConstField(int input) : assert(input > 0), value = input;
  
}

1.3 调用父类的构造函数

class Parent {
  
  int value;
  
  Parent(int input);
  
}

class Child extends Parent {
  
  Child(int input) : assert(input > 0), super(input);
  
}

在有初始化列表的情况下,函数调用的先后顺序为:

  • 子类初始化列表
  • 父类构造函数
  • 子类构造函数

二、const 构造函数

const构造函数可以保证你的类称为永远不会更改的对象,并使这些对象称为编译时常量:

  • 定义const构造函数确保所有实例变量都是final的。
  • 构造函数不能有函数体,但是可以有初始化列表。
class ConstConstructor {
  
  final value;
  
  const ConstConstructor(int input) : value = input;
  
}

三、实例变量的赋值

3.1 使用this简化赋值

class Subject {
  
  int value;
  int value2;
  
  Subject(this.value, this.value2);
  
  log() {
    print('value=$value, value2=$value2');
  }
  
}

void main() {
  Subject(2,3).log();
}

3.2 使用可选参数列表赋值或提供默认值

当我们希望 只为某些变量赋值,其余的采用初始值 时,在Java中只能采用定义多个构造函数或者使用builder模式,在Dart中采用可选参数列表的方式实现就很简单,调用的时候只需要采用 参数名:值 的形式赋值即可。

class Subject {
  
  int value;
  int value2;
  
  Subject({this.value, this.value2 = 3});
  
  log() {
    print('value=$value, value2=$value2');
  }
  
}

void main() {
  Subject(value : 2).log();
}

四、getter & setter

getset是专门用来读取和写入对象的属性的方法,每一个类的实例变量都有一个隐式的getter和可能的setterfinal或者const的只有getter)。

setter/getter使得我们在外部可以像对待普通成员变量进行操作,但是在内部进行额外的处理。

set/get后跟的是成员变量的名字,不可以与其它成员变量重名。

class Person {
  
  String _name;
      
  set name(String value) {
    assert(value != null);
    _name = value;
  }
  
  get name => 'Person name is $_name';
  
}

void main() {
  Person p = Person();
  p.name = "tom";
  print(p.name);
}

五、factory 修饰的构造函数

factory 用于修饰类的构造函数

例如,我们有一个Shape类,当我们用factory修饰:

factory Shapre() { return ...; }

再通过Shape()或者new Shape()的方式调用该函数时,不会真正地创建Shape,而是由该构造函数返回一个Shape或者Shape的子类。

它相当于为类提供了一个静态的创建方法,该方法的返回值是它本身或它的子类对象,但是外部并不知道这个方法的存在,只需要按照普通创建对象的方式创建即可。

根据这一特点,目前factory的应用有两类:

  • 实现工厂模式。
  • 单例模式。

5.1 工厂模式

Java中工厂模式的实现可以参考这里,工厂模式,下面我们Dart的实现版本。

class Shape {
  
  factory Shape(String shapeType) {
    if (shapeType.compareTo('CIRCLE') == 0) {
      return Square();
    } else {
      return Circle();
    }
  }
  
  void draw() {}
}

class Square implements Shape {
  
  @override
  void draw() {
    print('Inside Square::draw() method.');
  }
}

class Circle implements Shape {
  
  @override
  void draw() {
    print('Inside Circle::draw() method.');
  }
}

void main() {
  Shape shape = Shape('CIRCLE');
  shape.draw();
}

5.2 单例模式

回想一下,在Java中实现单例模式时,一般会将它的构造方法声明为private,再定义一个getInstance()的静态方法来返回单例对象。

而在Dart中,利用factory的特性,我们可以仍然按普通的方式创建对象,但是在多个地方获得的是同一个实例。

class Singleton {

  //单例对象。
  static Singleton _instance;

  //构造函数。
  Singleton._internal() {
    print("_internal");
  }

  static Singleton _getInstance() {
    //仅在 _instance 为空时才调用。
    if (_instance == null) {
      _instance = Singleton._internal();
    }
    return _instance;
  }
  
  //调用方法。
  void method() {
    print("method");
  }

  //1.通过 new Singleton() 调用。
  factory Singleton() => _getInstance();
  //2.通过 Singleton.instance 调用。
  static Singleton get instance => _getInstance();

}

调用方式有如下两种,_instance都会只创建一次:

Singleton.instance.method();
Singleton().method();

六、命名构造函数

Java中,可以声明多个构造函数,但是会遇到一个问题,如果有多个构造函数接受的参数个数和类型都相同,那怎么办呢?Dart的命名参数就解决了这一个问题。

除此之外,Dart的命名构造函数可以使得含义更加直观。

class Point {
  
  int x1;
  int y1;
  
  Point({this.x1, this.y1});
  
  Point.coordinate100() {
    x1 = 100;
    y1 = 100;
  }
    
  Point.coordinate50() {
    x1 = 50;
    y1 = 50;
  }
  
  log() {
    print('x=$x1, y=$y1');
  }
  
}

void main() {
  Point p100 = Point.coordinate100();
  p100.log();
  Point p50 = Point.coordinate50();
  p50.log();
}

这里需要注意,当我们声明了命名构造函数,那么就会 覆盖默认的无参构造函数,这点和声明了普通的有参构造函数是一样的。也就是说,在它子类的构造函数中,需要通过super.xxx显示地调用父类中的构造函数。

七、操作符

Dart中有一些操作符和Java不同。

7.1 类型操作符

  • 判断对象是否是指定的类型,使用is或者!is,类似Java中的instanceof
void main() {
   int n = 2;
   print(n is int);
}

void main() {
   double  n = 2.20;
   var num = n is! int;
   print(num);
}
  • a as T:将a转换称为指定的类型T,类似于Java中的(T)a

7.2 赋值运算符

  • ??=:仅在变量为null时才分配。
void main() {
  int a;
  a ??= 1;
  a ??= 2;
  print(a);
}

运行结果:

1

7.3 条件表达式

  • expr1 ?? expr2:如果expr1不为null,那么返回expr1的值,否则返回expr2的值。
void main() {
  String a;
  String b = a ?? "default";
  print(b);
}

运行结果:

default

7.4 条件成员访问:判空+调用

  • ?.:仅当对象不为空时才执行后面的操作,省去了Java中先判空,再去调用方法的步骤。
class Person {
  
  String name;
  
  Person(this.name);
      
  log() {
    print(name);
  }
  
}

void main() {
  Person p;
  String name = p?.name;
  p?.log();
  p = Person('a');
  p.log();
}

7.5 级联操作符

..允许对同一对象执行一系列操作,可以省去创建临时变量的步骤。

class Person {
  
  String name;
  
  Person(this.name);
      
  log() {
    print(name);
  }
  
}

void main() {
  Person p = Person('a');
  p..name = 'b'
    ..log();
}

八、函数也是一种类型

Flutter中,有很多将Function也就是方法作为成员变量的使用,其作用类似于Java当中的回调,当我们的回调接口中只有一个抽象函数时,可以使用这种方法代替。

typedef CalBuilder = int Function(int x, int y);

class Cal {
  
  CalBuilder builder;
  
  Cal(this.builder);
      
  log() {
    print('result=${builder(1, 2)}');
  }
  
}

void main() {
  Cal cal = Cal((a, b) => a + b);
  cal.log();
}

九、函数的可选参数

当函数执行时不需要强制传递参数时,可以使用可选参数,可选参数时函数中的最后一个参数。

9.1 可选位置参数

可选参数的语法为:

void function_name(param1, [param1, param2]) { }

示例如下:

void main() {
  func(1);
  func(1,'2');
}

void func(int a, [String b, int c]) {
  print(a);
  print(b);
  print(c);
}

注意:如果当赋值给可选参数的实参数目不够时,那么必须按照形参类型定义的顺序依次赋值,否则无法编译通过。

9.2 可选命名参数

与可选位置参数不同,可选命名参数要求必须在传递值时指定参数名称。

void function_name(a, {param1, param2}) { }

示例:

void main() {
  func(1);
  func(2, c:3);
}

void func(int a, {String b, int c}) {
  print(a);
  print(b);
  print(c);
}

9.3 带有默认值的可选参数

无论是可选位置参数还是可选命名参数,都可以定义默认值。语法为:

function_name(param1, {param2 = default_value}) { }

示例:

void main() {
  func(1);
  func(1,'2');
}

void func(int a, [String b = 'default', int c]) {
  print(a);
  print(b);
  print(c);
}

输出结果:


1
default
null
1
2
null

十、容器

Dart中,常用的容器有ListMap,在使用上和Java也有一些区别。可以参考下面的两篇文章,就不列举了:

参考文章

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345