Article
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!
Related
Related posts
-
conjure-dart 更新:别名类型 alias 代码生成实现
2025-11-16
-
MVC 常用常新,温故知新:纵你虐我千百遍 我仍待你如初见
2025-09-10
-
Conjure实战:从零搭建前后端分离的RPC服务
2025-07-21
-
Conjure实战:对接高德导航 API(驾车导航)
2025-07-21