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