跳到正文
W Winse Blog
dev mobile 3 min read

Dart入门手记:Java程序员,你还好吗?

相比于从 Typescript开发 转过来的,Dart 对于 Java程序员 来说是比较友好的,Dart是更加严格的面向对象的语言,null都是对象Null的实例,可以调用toString方法。

但如果你就天真的对 友好 二字太信赖,也是会让你吃哑巴亏的。这里记录下让自诩Java老程序的我直呼反直觉的那些坑。

# 整数

# 除法

Dart中分为 除/ ,和 整除~/ 两个操作符。

Java中根据类型来区分,整数除整数的结果是整数,其他除的结果都是浮点数的,long/float取决于里面大的类型。

Dart中 除/ 的结果都是double;整除 的结果都是int,浮点 整除 浮点结果也是int 的。

      // 整数除整数       print(6 / 2); // 3.0       print(6 ~/ 2); // 3       print(6 ~/ 5); // 1       print(6 ~/ 7); // 0       // 浮点数除整数       print(6.0 / 2); // 3.0       print(6.0 ~/ 2); // 3       // 浮点数除浮点数       print(5.0 / 2.5); // 2.0       print(5.0 ~/ 1.5); // 3       print(5.0 ~/ 2.5); // 2       print(5.0 ~/ 5.5); // 0

# 符号位

由于dart中没有细分整数的byte、short、int、long。就只有int64,这就会出现在接口对接时,符号位不同导致的尴尬问题。

我们知道Color是由ARGB凑成一个32位整数来存储的。如果接口使用数字来传递有透明度Color的话,如果最高位31位为1,在Java中就是负数了,但是在Dart的int是64位的,还是正整数的。所以这种就要做特殊处理。

extension ColorExt on Color {   int get protoValue {     final i = this.value;     return i < 0x7fffffff ? i : (i & 0x7fffffff) - (i & 0x80000000);   }   static fromProto(int value) {     if (value >= 0) {       return Color(value);     } else {       return Color((value & 0xFFFFFFFF) | 0x80000000);     }   }

# 取余

在 Java 中,a % b 表示取余(truncated remainder),结果的符号与被除数 a 保持一致

        System.out.println(5 % 3); // 2         System.out.println(-5 % 3); // -2         System.out.println(5 % -3); // 2         System.out.println(-5 % -3); // -2

在 Dart 中,% 表示欧几里得模(Euclidean modulo),结果总是非负。

Dart 中如果需要与被除数符号一致的余数,需要使用 remainder(与java的%含义一致):

      // 取模 / 取余,始终为正(或者0)       print(5 % 3); // 2       print(-5 % 3); // 1       print(5 % -3); // 2       print(-5 % -3); // 1       // 余数, 符号和被除数 a 一致       print(5.remainder(3)); // 2       print((-5).remainder(3)); // -2       print(5.remainder(-3)); // 2       print((-5).remainder(-3)); // -2

# 字符串拼接

字符串拼接方面,Dart比Java灵活太多了,除了 +加号 只能处理字符串,还有字符串插值;有三引号的字符块;以及原始字符串 写法就不用写转义了;还有拼接时竟然加号都可以省略(字面量的自动拼接),这你敢信嘛。

      var name = "bob";       var age = 18;       // 字符拼接       var message2 = "my name is " + name + ", age is " + age.toString();       // 字符串插值       var message3 = "my name is ${name}, age is ${age}";       // 字符块       var message4 = """       my name is $name,       age is $age       """;       /// 原始字符串,省了转义       var path = r"C:\Program Files\MyApp\data.txt";       // 字面量的自动拼接       var message = "my name is "//           "winse"           ", gender is "           "man";

# 相等

Java中判断值相等使用equals,Dart使用操作符operator ==。Java的内存对象相等使用==,Dart使用identical。如果批量转换Java代码为Dart的话,那真是头痛加手痛啊那酸爽了。

但比较意外的是,每次获取的方法实例竟然都是不同的。

class A {   void say() => print('hi'); } void main() {   final a = A();   final f1 = a.say;   final f2 = a.say;   print(identical(f1, f2)); // ❌ false!   print(f1 == f2); }

所以咯,在观察者模式中的判断观察者相等,不要用identical,直接使用 操作符== 判断。List判断元素是否相等也是使用的 操作符== 。so,直接用 List 的 contains 等操作就行了。

final List<HoverListener> _hoverListeners = []; void addHoverListener(HoverListener listener) {     if (!this._hoverListeners.contains(listener)) {       this._hoverListeners.add(listener);     }   } void removeHoverListener(HoverListener listener) {     var index = this._hoverListeners.indexOf(listener);     if (index != -1) {       this._hoverListeners.removeAt(index);     }   } void _fireHover(Element? previous, Element? hovering) {     for (var listener in this._hoverListeners) {       listener(previous, hovering);     }   }

# 初始化

Dart看起来是懒加载的,没用到就不会初始化。看下面的例子,使用时 的第一个TOOLTIP初始序号为0,而不是代码中看起来的BACKGROUND。

so,不要依赖于成员变量在代码中看起来的顺序

class Layer {   static int _incrementSeq = 0;   final int ordinal;   final String name;   Layer(this.name) : this.ordinal = _incrementSeq++;   static final BACKGROUND = Layer('background'); // 0?   static final TEXT = Layer('text'); // 1?   static final POPUP = Layer('popup'); // 2?   static final TOOLTIP = Layer('tooltip'); // 3? } void main() {   print(Layer.TOOLTIP.ordinal); // 0   print(Layer.TEXT.ordinal); // 1   print(Layer.POPUP.ordinal); // 2   print(Layer.BACKGROUND.ordinal); // 3 }

# call方法

在调用 可为空函数 的时刻,一般使用func?.call()去执行。

其实类里面也可以定义call 这个方法,这样类实例就可以用instance()像方法一样 去调用了。这真的是你中有我,我中有你。

import 'package:flutter/material.dart'; class Add {   void call(int a, int b) => a + b; } void main() {   VoidCallback? cb = null;   cb?.call();   final add = Add();   print(add(1, 2)); }

# 异步

严格来说,Java没有Dart中的async异步,Java异步代码都是在线程里面跑的。

但Dart的async方法则是运行到await这一行才是真正的异步开始,之前的代码都是同步执行的。参见下图右边的Dart代码输出12345,没想到吧:

Future工厂方法含义是不同,使用时注意下,它里面实际包了个Timer的:

# 想想

当然了Dart还有很多很多不同于Java的:

* 修饰符:没有private使用变量前加 _下划线 来实现,所有默认都是public,没有protected。
* 没有明确的interface接口,但又可以理解所有的类都可以是接口。有mixin,extension。
* 有 part 和 part of 可以将一个功能拆分到多个文件,同时保持它们属于同一个库。
* 不推荐反射,更多推荐是提前把代码都生成 出来调用。
* 日期中的月份month是实打实的从1开始,不是0哦。
* 创建map{}和list[]时,内部可以使用if和for来构造元素。
操作符±*/可以重载, 如:"-" * 10 。

还有很多,脑子不够用就写这么一些了。

尽管有诸多差异,但是对于Java程序员来说,入门Dart确实是容易的,多写多实践 个把星期就能熟练使用了。

再加上AI的加持,Dart生态的依赖库 这块也不是什么问题。要实现一个功能,先问问AI有没有现成的库,没有就让它先写一个基础的,再在这个基础上面去改。

入门Dart,so easy!

在 GitHub 上讨论

欢迎通过 GitHub Issue 留言或反馈。每条讨论都会关联到对应文章的源文件路径。

2025-07-17-Dart入门手记:Java程序员,你还好吗?.md

Related posts