Dart for Java Developers
A deep, practical mapping from Java to Dart: syntax, type system, concurrency, tooling, Flutter, and backend (Shelf, Relic, Serverpod, and the broader pub.dev ecosystem). This guide is written for engineers who already know Java and want fast mental-model transfer plus honest gaps (what one language has that the other does not).
Note on sources: This guide draws on Learn Dart in Y Minutes (community tutorial; the bundled
learndart.dartmixes teaching with legacy phrasing—prefer this guide + dart.dev for sound null safety facts) and Google’s archived Flutter codelab Intro to Dart for Java Developers (Bicycle → Rectangle → factories → functional iteration).
Table of contents
- Mental model in one page
- Why Dart feels “Java-like” (and where it diverges)
- Crash-course synthesis (Learn X in Y Minutes and From Java to Dart codelab)
- Timeline: Dart history & language evolution
- Syntax & structure
- Type system & null safety
- Functions, methods, parameters
- Classes, constructors, factories
- Inheritance, interfaces, mixins, extensions
- Generics & variance (Java vs Dart)
- Enums, sealed classes, patterns (Dart 2.17–3.x)
- Collections & iteration
- Errors & exceptions
- Asynchrony:
Future,Stream,async/await, isolates - Modules, libraries,
part,export, packages - Tooling: pub, analyzer, formatter, tests
- Compilation targets & runtime (vs JVM)
- Flutter (what Java devs need first)
- Backend on Dart: Shelf, Relic, Serverpod, Dart Frog, gRPC
- Common / notable libraries (selected)
- Feature matrix: Java ↔ Dart
- What exists in one language but not the other
- Migration heuristics
- Glossary: Java ↔ Dart terminology
- Keywords quick map
- Operators (selected) with Java notes
dart:*libraries — Java mental anchors- Class modifiers (Dart 3)
- Patterns, destructuring,
switchexpressions - Callable objects & operator overloading
typedefand type aliases- Dates, time zones, internationalization (
intl) - Regular expressions
- URIs, networking, encoding
- Binary data:
typed_datavs NIO - Reflection, codegen, and why Flutter differs
- Annotations & metadata
- JSON / immutable data classes
- Local databases & ORM-ish
- Dependency injection & global state
- Build systems: Maven/Gradle ↔ pub
- Testing landscape (beyond JUnit)
- Package catalog (extended)
- Expanded feature matrix
- Soundness footguns
- Further reading
- Generators: sync* / async* / yield
- Iterable, Iterator,
for-invs Java - Deferred & conditional imports
dart:ffivs JNI (sketch)- Super parameters & initializer lists
- Flutter layering (Widget / Element / RenderObject)
- Numeric types & compilation targets caveat
- Appendix: Java ↔ Dart cheat sheet (snippets)
Mental model in one page
| Idea | Java | Dart |
|---|---|---|
| Primary runtimes | JVM (bytecode), Graal native | VM (JIT dev), AOT (release native), also JS & Wasm targets for web |
| Unit of compilation / reuse |
.java → packages + modules (JPMS optional) |
Every .dart file is a library; packages on pub.dev |
| Null | Nullable reference types (Java 8+ annotations; JEP 804 modern nullable types in recent Java) |
Sound null safety (non-null by default); T? for nullable |
| Concurrency | Threads, ExecutorService, virtual threads (Project Loom, Java 21+) |
Single-threaded event loop in isolate + explicit isolates (no shared mutable heap) |
| OOP style | Classes + interfaces | Classes + implicit interfaces + mixins |
| Polymorphism tricks | Abstract class, default interface methods | Extension methods, mixins, factory constructors |
| Async |
CompletableFuture, reactive stacks |
Future/Stream, async/await first-class
|
| Builds | Maven / Gradle |
dart pub + SDK tools; Flutter adds its build |
| “Enterprise” servers | Huge ecosystem | Growing: Serverpod, Relic, Shelf, etc. |
Bottom line: Dart optimizes for fast iteration, UI (Flutter), and multi-target compilation. Java optimizes for massive enterprise libraries, JVM depth, and decades of JVM tooling.
Why Dart feels “Java-like” (and where it diverges)
What feels immediately familiar
-
C-family syntax: curly blocks, semicolons (usually optional in Dart but often used), similar control flow (
if/for/while/switch). -
Strong, static typing with type inference (
var/finalvs Java’svarsince Java 10). - OOP core: classes, inheritance, abstract classes, (most) methods live on types.
-
Garbage collection (Dart VM) — no manual
free.
What surprises Java programmers first week
| Topic | Java intuition | Dart reality |
|---|---|---|
new |
new Foo() everywhere |
new optional / uncommon in modern style: Foo()
|
| One class per file | Convention |
Library = file; part / part of split implementation |
| Overloading | Common | No method overloads — use named args, different method names |
static utils |
Collections, StringUtils, … |
Less culture of static bags — top-level functions, extensions |
| Null |
@Nullable, Optional, NPE |
Types express null (String vs String?) — sound |
| Threads | Use freely (with care) | Isolate model; no shared memory between isolates |
| Checked exceptions | throws |
No checked exceptions — errors are unchecked style |
| Wildcard generics | List<? extends Number> |
Dart generics different (see generics section) |
Crash-course synthesis (Learn X in Y Minutes and From Java to Dart codelab)
External primers worth bookmarking:
-
Learn Dart in Y Minutes — fast runnable
learndart.darttour: comments,var/final/const, nested functions,Iterable/List/Mapshapes ((),[],{}), loops,~/, generators, mixins, cascades, null-aware operators. Caveat: some prose predates sound null safety; treat runtime/typing claims in that file with skepticism and follow Dart 3 rules here. -
Google Flutter codelab — Intro to Dart for Java Developers — pedagogy from the Java Tutorial’s
Bicycle→ Dart:mainat top level,newoptional,this.fieldconstructor shorthand, privacy via_, getters, no constructor overloading replaced by named parameters / defaults,factoryconstructors,implementswithoutinterfacekeyword,map/forEachfunctional style.
One-screen facts (battle-tested when you start Dart / Flutter)
| Topic | Dart (what to remember) |
|---|---|
Module = .dart file |
A library is the file; top-level functions, variables, const, and classes coexist (like Kotlin files). |
| Library privacy | Names starting with _ are private to the library (the .dart file / merged part), not “private to the class” like Java private. |
| Everything is an object |
int, double, bool, Null — unified model; no Java-style primitive-only stack semantics. |
| Null safety | Append ? for nullable types (String?); flow analysis narrows types — Kotlin-like, not optional typing. |
| Generics & reification | Type arguments to generic classes are reified (List<int>.runtimeType knows int). Java erases type parameters at runtime (with erasure quirks + reflection metadata). Footnote: generic function instantiations still have limits—don’t treat runtime reflection as “full templates.” |
| Inference |
var x = 1; — var is like Java 10+ var (inferred fixed type, not dynamic reassignment to another type). |
final vs const |
final = single assignment (runtime). const = compile-time constant object graph (must be constructible const). |
| Imports |
dart: SDK (import 'dart:convert';), relative (import 'src/foo.dart';), package: (import 'package:test/test.dart';). Use as, show, hide, deferred as to tame namespaces. |
| Async |
async/await, Future<T>, Stream<T> on the current isolate; heavy CPU → isolates (dart:isolate). |
| Generators |
sync* / async* + yield / yield* for lazy iterables and async streams. |
| Callable classes | Implement call so myObj(x) works like a function. |
typedef |
Type aliases + function types: typedef Pred = bool Function(Event e);
|
| Control flow |
for (e in xs), while, do-while; switch in Dart 3 supports patterns / exhaustiveness (unlike Java’s old switch only story). |
| Extensions |
extension X on String { ... } — static dispatch on static type of receiver (like Kotlin). |
| Immutability codegen | See freezed on pub.dev for union types + copyWith (think “data classes + sealed” ergonomics). |
Timeline: Dart history & language evolution
Dates are approximate release periods; exact patch versions vary. For authoritative per-version changes, see Dart language evolution.
Early era
-
2011: Dart unveiled; goals included structured web programming and optional typing (legacy “Dart 1” allowed
dynamicescape hatches broadly). - Dart 2 (2018): Strong typing becomes central; sound type system push begins.
Null safety and Dart 3
- Dart 2.12 (2021): Sound null safety ships (opt-in migration with tools).
- Dart 3 (2023): Null safety mandatory; removal of many legacy modes; language cleanup for long-term evolution. See: The road to Dart 3.
Modern language features (selection)
| Era | Features (examples) |
|---|---|
| Dart 2.17 | Enhanced enums (fields, methods, constructors) |
| Dart 2.19 / 3.0 prep |
Records, patterns, class modifiers (sealed, final, interface, mixin class) |
| Dart 3.0 |
Records (x, y), pattern matching in switch, sealed types, destructuring |
| Dart 3.x | Incremental improvements (macro experiments, documentation updates, analyzer) |
| Recent SDK lines | Productivity: dot shorthands and tooling (see current SDK changelog for your version) |
Java parallels for the same decades: lambdas (Java 8), modules JPMS (9), var (10), records (16), pattern matching for switch (17–21), virtual threads (21), etc. Dart moved quickly on UI-driven language features (e.g. records/patterns) because Flutter benefits directly.
Syntax & structure
Entry point
Java
public class Main {
public static void main(String[] args) {
System.out.println("Hello");
}
}
Dart
void main() {
print('Hello');
}
Top-level main is idiomatic. print is a core library function (not a static on System.out).
“Everything is an object” (both—but differently)
-
Java: primitives (
int,double, …) are not objects; wrappers exist. -
Dart: no primitive types in the Java sense;
int/double/boolare types with fixed-size semantics but also objects in the unified type model;numis a supertype ofintanddouble.
Semicolons
Dart allows omission via line ending rules (Dart style often omits semicolons outside for heads etc.).
Strings
-
Single quotes
'...'and double"..."are both string literals; interpolation:'$name ${obj.field}'. - Raw strings:
r'\no escape'. - Multiline:
'''...'''or"""...""".
Type system & null safety
Non-null by default (sound)
-
Stringcannot holdnull. -
String?can holdnull. - Promotions and flow analysis narrow types after checks (similar spirit to Kotlin’s smart casts, not Java’s old unchecked world).
Common operators
| Operator | Meaning |
|---|---|
?. |
null-aware member access |
?? |
null-coalesce |
??= |
assign if null |
! |
null assertion (“I assert not null”—avoid when possible) |
required |
named parameter must be provided (with null safety expectations) |
Java mapping
| Java | Dart |
|---|---|
@Nullable String |
String? |
Guava Optional<String> for presence |
Prefer String? or domain types; Optional-like patterns exist in packages but are not idiomatic core |
| NPE on dereference |
Compile-time help + runtime checks only when unsound escapes (dynamic, invalid !) |
dynamic vs Java Object
-
dynamicopts out of static checks (similar in spirit to very loose typing; unlike usingObjectwith casts everywhere). - Prefer typed APIs; use
Object?when you need “anything” but keep static checks.
Functions, methods, parameters
No overloading
Java
void draw(int x) {}
void draw(int x, int y) {}
Dart — use named parameters, optional positional, or different names:
void draw(int x, [int? y]) { }
void drawNamed({required int x, int? y}) { }
void drawVertical(int x) { }
Named parameters
Named args are huge in Dart APIs (Flutter widget constructors are the famous example):
Widget build() => Padding(
padding: const EdgeInsets.all(8),
child: Text('Hi'),
);
Think: readable call sites > Java’s builder / telescoping constructors patterns.
First-class functions
-
Function types:
int Function(int, int)vs Java’s functional interfaces (IntBinaryOperator, etc.). -
Tear-offs:
list.sort(compareTo)passes methods as values naturally.
Classes, constructors, factories
const constructors
Dart has true immutable const objects when types support const constructors — compile-time constants. Java’s final + static is related but not the same model.
Factory constructors
class Singleton {
Singleton._();
static final Singleton instance = Singleton._();
}
Also idiomatic: factory constructors that may return cached instances or subtypes (think similar to static factory methods in Java EnumSet.of, List.of).
Redirecting constructors
class Point {
final int x, y;
Point(this.x, this.y);
Point.origin() : this(0, 0);
}
Inheritance, interfaces, mixins, extensions
Implicit interfaces
Every class exposes an implicit interface of its instance methods. Java: implements requires explicit interface type. Dart: implements Foo where Foo is a class is normal.
Mixins
Java: default interface methods + composition patterns. Dart: mixin + with apply reusable behavior without inheritance diamonds complexity of C++.
mixin Walks {
void walk() => print('walk');
}
class Person with Walks {}
Restrictions tightened with mixin class / class modifiers in Dart 3 — favor composition when modeling “capabilities.”
Extension methods
Java: preview/stable extension methods are newer and limited by platform. Dart: extension widely used (e.g. Flutter context.push style helpers in packages).
extension StringX on String {
String twice() => this + this;
}
Generics & variance (Java vs Dart)
Covariance in core collections (important surprise)
Dart: List<String> is a subtype of List<Object> (if that still sounds wrong for a Java brain: in Dart’s sound variance rules for interface types, List is covariant in its type argument). That enables natural UI APIs but implies runtime checks on writes in some cases.
Java: at compile time List<String> is not a List<Object> for mutable lists (List<String> vs List<? extends Object> wildcards).
Practical advice: when porting Java mental models, re-read generic collection APIs and avoid unsound casts; trust the analyzer.
Wildcards
Java ? extends T, ? super T have no direct counterpart as a unified wildcard system; Dart uses upper bounds and ** typedefs / type parameters** differently. Many Java wildcard use cases become specific type parameters or helper methods.
Enums, sealed classes, patterns (Dart 2.17–3.x)
Enhanced enums (Dart 2.17+)
enum Status {
ok(200),
notFound(404);
final int code;
const Status(this.code);
}
Java enums are strong too; Dart’s are now very expressive with fields and methods.
Sealed / exhaustive switch
Dart 3 sealed class hierarchies enable exhaustive switch—closer to algebraic data types in expression form.
Java 21 modern switch + sealed types — conceptual siblings; syntax differs.
Records (both languages)
-
Java 16+
record Point(int x, int y) {} -
Dart 3
(int x, int y)record types — great for multiple returns without defining a full class.
Collections & iteration
Core in dart:core: List, Map, Set, Iterable.
| Java | Dart |
|---|---|
ArrayList |
List (literal [1,2], growable vs fixed) |
HashMap |
Map (literal {'a':1}) |
HashSet |
Set |
Streams API (Stream<T> in java.util.stream) |
Two stream worlds: synchronous Iterable + async Stream<T> (see async section) |
Collection literals are idiomatic and compile to efficient code.
Errors & exceptions
No checked exceptions
Java: checked IOException shapes API contracts. Dart: typically throws documentation + Future error channels — no throws clause enforced.
Error vs Exception
Dart has a loose convention:
-
Error: programmer mistakes / assertions / should fix code. -
Exception: recoverable failures.
Neither is checked.
Asynchrony: Future, Stream, async/await, isolates
Futures vs CompletableFuture
Dart Future<T> is the baseline async result. async/await syntax maps closely to Java’s async/await if you use frameworks that add it, but in Dart it’s language-native.
Streams vs Java reactive streams
Stream<T> is async sequences. Think RxJava-like operations exist in stream_transform, rxdart, but core Stream is smaller than Project Reactor.
Important: single subscription vs broadcast streams — learn early when wiring Flutter (e.g. UI listeners).
Isolates vs threads
| Java | Dart |
|---|---|
| Threads share heap (with locks) | Isolates have separate heaps — no shared mutable state |
| Synchronization primitives |
message passing (SendPort/ReceivePort), Isolate.run patterns |
| Loom virtual threads | Isolates are not lightweight threads of the same sort |
Mental model: Flutter UI runs on main isolate event loop; heavy CPU work should move to workers (compute, isolates) to avoid jank.
Modules, libraries, part, export, packages
File = library
A .dart file is a library (optionally named).
part / part of
Splits one library across files (often for generated code: *.g.dart with build_runner).
export
Barrel files re-export other libraries (similar to Java aggregator patterns but simpler).
Packages (pubspec.yaml)
Think pom.xml / build.gradle + coordinates, but much leaner:
- Dependencies: hosted on pub.dev (and git/path overrides).
- Dev dependencies: generators, tests, lints.
Version solving is declarative (pub resolver).
Private Dart members: leading underscore _detail is library-private (not private keyword).
Tooling: pub, analyzer, formatter, tests
| Need | Java common | Dart |
|---|---|---|
| Build | Maven / Gradle |
dart pub get, dart run, dart compile
|
| Annotation processing / codegen |
javac -processor, Gradle annotationProcessor
|
dart run build_runner build with build / source_gen generators — no built-in “APT” keyword; this is the usual Java annotation-processing substitute |
| Format | Spotless / Checkstyle |
dart format (often CI-enforced) |
| Lint | Checkstyle / Error Prone |
analysis_options.yaml + package:lints / flutter_lints
|
| Test | JUnit 5, AssertJ |
package:test, flutter_test
|
| Coverage | Jacoco | SDK / package coverage |
Analyzer & language server
The Dart analyzer powers IDE experience (similar spirit to IntelliJ/Java LS but often remarkably fast for incremental analysis).
Compilation targets & runtime (vs JVM)
| Mode | What it means for a Java dev |
|---|---|
| JIT VM (dev) | Fast reload (Flutter hot reload changes UI state carefully) |
| AOT native | Release builds without shipping a VM bytecode interpreter (platform-dependent) |
| dart2js / Wasm | Web targets — no JVM |
GraalVM native image is the rough Java-side analogy for AOT, but pipeline details differ a lot.
Flutter (what Java devs need first)
Flutter is not a language change—same Dart language. What changes is:
- Widget build/rebuild model (immutable-ish widget configs vs mutable Swing/JavaFX views in typical old UIs).
-
Composition over deep inheritance trees (
Widgettrees). -
State management libraries explode (
provider,riverpod,bloc,get_it, …) — maps to Spring-ish DI only by analogy. - Platform channels ↔ JNI-like bridges to native OS APIs.
Hot reload is a killer feature for UI iteration; Java UI historically relied on slower full restarts (with exceptions in some frameworks).
Backend on Dart: Shelf, Relic, Serverpod, Dart Frog, gRPC
Shelf
Minimal shelf middleware model — like a small servlet filter chain or Ring-style handler stacks.
Relic
Relic (from the Serverpod team) is a modern HTTP stack positioned as a next-gen basis with strong typing, routing, WebSockets, static files—Serverpod builds on it. If you see “Relic” in 2026-era Dart docs, read it as framework plumbing, not archaeology.
Serverpod
Serverpod aims at full-stack Dart: server ORM, migrations, tooling, code generation, integrated auth patterns—closest spirit to Rails/Django/Laravel (still maturing vs Spring’s 20-year ecosystem).
Dart Frog
Lightweight server from Very Good Ventures—good for APIs and BFF layers.
Aqueduct
Historical note: Aqueduct was popular years ago; check current maintenance before new projects—ecosystem moves fast.
gRPC / protobuf
Official grpc Dart package exists—good interop with Java microservices.
Comparison tone
- Spring Boot maturity: unparalleled Java depth.
- Dart backend: excellent fit when sharing models/validation with Flutter or reducing polyglot—evaluate library freshness, team skills, and ops story.
Common / notable libraries (selected)
This is not exhaustive—pub.dev has massive breadth. Treat as signposts.
| Area | Packages / notes |
|---|---|
| HTTP client |
http, dio (interceptors, richer client features) |
| JSON serialization |
json_serializable + build_runner, freezed immutable unions |
| Routing (Flutter) |
go_router, auto_route, … |
| State (Flutter) |
provider, riverpod, bloc, … |
| Functional helpers |
dartz (Either, Task): Haskell-flavored—love/hate |
| Logging |
logging package standard interfaces |
| Env/config |
flutter_dotenv, custom patterns |
| Tests mocking |
mockito, mocktail
|
| Concurrency helpers |
pool, isolate wrappers |
| DB (Dart server) |
postgres, mysql drivers; ORM layers vary by framework |
| Redis etc. | community packages—verify maintenance |
Serverpod bundles many server concerns opinionated.
Feature matrix: Java ↔ Dart
| Feature | Java | Dart |
|---|---|---|
| Primitive types | Yes | Unified object model (int, double, …) |
| Checked exceptions | Yes | No |
| Method overloading | Yes | No |
static class file plumbing |
import static |
Top-level functions idiomatic |
| Wildcard generics |
? extends, ? super
|
Different variance model |
| Protected visibility | protected |
No—library privacy with _
|
| Package-private | Yes | Library scope via _ + library boundaries |
| Multiple inheritance of classes | No | No—but mixins and mixin class patterns |
| Interface default methods | Yes | Default instance methods on classes; extension
|
enum power |
Strong since Java 5; evolved | Enhanced enums great |
switch exhaustiveness |
Improved in modern Java |
sealed + patterns strong |
record |
Java 16+ | Dart 3 records |
| Null safety in type system | Modern Java + annotations | ? types + soundness |
| Threads | Rich | Isolates + async event loop |
| VM / native / web | JVM primary | VM, AOT, JS, Wasm |
| Mobile UI | Android (Kotlin often) | Flutter dominant Dart UI |
What exists in one language but not the other
Java strengths not matched 1:1 in Dart
- JVM ecosystem depth (transactions, enterprise specs, vendor integrations).
- Mature concurrent libraries tuned for shared-memory threading (dart is different concurrency model on the UI side).
- JPMS modularization story (Dart uses packages + visibility).
-
Graal + gigantic JNI/native interop stories in places (Dart has
dart:ffifor C interop instead).
Dart strengths / conveniences
- Hot reload ecosystem (Flutter).
- UI-declarative widget model + one language across mobile/web/desktop (with caveats).
-
Low ceremony (
pub, formatter, analyzer). - Structural patterns (extensions, mixins, named parameters) that reduce boilerplate without annotation processors everywhere.
Migration heuristics
- Stop designing overload sets — reach for named params and small types.
-
Model absence with
T?, notnulleverywhere + comments. -
Embrace
async/await—avoid callback pyramids like avoiding rawCompletableFuturechains in modern Java. - Learn isolates before porting “background thread does shared state” Java—rethink shared memory.
-
Learn
List/Mapvariance rules before performance-critical numeric code with heavy generics. -
Use
package:lints/flutter_lintsearly—Dart’s analyzer is your pair programmer. -
Prefer immutable models (
finalfields,freezedwhere helpful) especially in Flutter UIs.
Glossary: Java ↔ Dart terminology
| Java concept | Dart counterpart / note |
|---|---|
package com.foo.bar |
package:foo_bar/foo_bar.dart + lib/ layout; import URI not a 1:1 filesystem mirror |
public |
Symbols are public to importing libraries unless _ private
|
private |
_leadingUnderscore = library-private (not class-private) |
protected |
No direct equivalent — prefer small public APIs, @visibleForTesting, or composition |
static nested class |
Often top-level private class in same library |
| Interface |
abstract class, interface class (Dart 3), or “implements a class’s interface” |
| SAM / functional interface |
Function types (int Function(int a)) |
static final constant |
static const, const constructors, top-level const
|
Reflection (java.lang.reflect) |
dart:mirrors on VM only — Flutter release/AOT avoided; use codegen
|
Annotation processing (javac -processor, Gradle annotationProcessor) |
build_runner + packages like source_gen — no separate JVM-style APT in the language; this is the default industrial substitute |
| JNI |
dart:ffi for C interop (plus package:ffi helpers) |
synchronized |
Prefer atomic single-isolate async patterns; isolates + message passing for parallelism |
volatile / Java memory model |
No shared mutable memory between isolates — immutable messages |
ThreadLocal |
Zone, Flutter InheritedWidget, or explicit parameter passing |
Module (module-info.java) |
package boundaries + exports; no JPMS verifier |
Keywords quick map
-
Java-only (concept):
synchronized,strictfp,native,transient,volatile,throwsas a contract keyword. -
Dart-only / distinctive:
mixin,extension,on(extension / mixin constraints),with(mixin application),factory,operator,external,covariant,yield,yield*(generator forms),late,required,async,await,dynamic,library,part,export,show,hide,deferred(deferred loading). -
Overlapping but different semantics:
interface(Dart 3 class modifier, not Java’sinterfacekeyword alone),super(mixins / super parameters),new(optional in Dart).
Operators (selected) with Java notes
For every predefined Dart operator token, precedence row, and Java analogue, see Appendix — Dart operators — complete predefined set (Java notes) (aligned with dart.dev/language/operators).
| Dart | Rough Java analog | Notes |
|---|---|---|
?. |
conditional navigation | null-aware member access |
.. / ?..
|
no single operator | Cascade: perform multiple ops on same receiver |
~/ |
integer division | truncating division for num
|
as / is / is!
|
cast / instanceof
|
prefer patterns when possible |
?? / ??=
|
null checks + assignment helpers | idiomatic defaults |
Postfix ! |
(Type)expr you hope is safe |
runtime failure if wrong |
=> |
lambda / concise body | Arrow bodies everywhere |
... spread |
List.copyOf, streams combine |
collection / map spreads in literals |
dart:* libraries — Java mental anchors
| Library | Purpose | Java anchor |
|---|---|---|
dart:core |
Core types, collections, Uri, RegExp, DateTime, errors |
java.lang + parts of java.util
|
dart:async |
Future, Stream, StreamController, zones |
CompletableFuture, reactive Publisher (not identical) |
dart:collection |
queues, linked maps, etc. |
java.util extras |
dart:convert |
JSON (JsonCodec), UTF-8, base64 |
Jackson spirit (not drop-in) |
dart:io |
VM & native I/O, HTTP server, sockets, File, Process
|
NIO + java.net + java.io
|
dart:html |
Browser DOM (transpiled) | JS / not JVM |
dart:js_interop |
JS interop (modern) | JNI for JS land |
dart:ffi |
C ABI interop | Panama / JNI (different) |
dart:isolate |
isolate spawn, ports | threads + message queues |
dart:math |
sqrt, Random, min, max
|
Math, Random
|
dart:typed_data |
Uint8List, views, ByteData
|
ByteBuffer + primitive arrays |
dart:developer |
timeline, logging, service extensions | JFR / JVMTI (much smaller surface) |
Class modifiers (Dart 3) — Java intuition
| Modifier | Intent |
|---|---|
final class |
Prevents subtyping outside the library (library-first design). |
sealed class |
Permitted subtypes only in same library — enables exhaustive switch. |
interface class |
Direct instantiation restricted — implement elsewhere. Controls construction, not just “interface-ness.” |
mixin class |
Declares something usable as mixin and as class. |
Java sealed uses permits and named subclasses anywhere in allowed modules. Dart sealed subtypes sit in the same library — different packaging tradeoff.
Patterns, destructuring, switch expressions (Dart 3)
Switch expressions return values; pattern cases match structures (records, object shapes):
sealed class Msg {}
class Ping extends Msg {}
class Pong extends Msg {}
String react(Msg m) => switch (m) {
Ping() => 'ping',
Pong() => 'pong',
};
void tupleDemo() {
final (x, y) = (1, 2);
print('$x $y');
}
Think Java’s modern switch + deconstruction converging—Dart 3 doubles down on switch as expression and algebraic shapes via sealed hierarchies + records.
Callable objects & operator overloading
call: an object can behave like a function:
class DoubleFn {
int call(int x) => x * 2;
}
Operators: implement operator + etc. Java lacks user-defined + for arbitrary classes.
typedef and type aliases
typedef JsonMap = Map<String, Object?>;
typedef IntPredicate = bool Function(int value);
Closest Java analogue: type parameter bounds + functional interfaces, not a first-class typedef.
Dates, time zones, internationalization (intl)
-
DateTimehandles instant vs local/utc flags; time zones usually needtimezonepackage or careful UTC storage. -
package:intlfor DateFormat, messages, plural rules — pair with Flutter localization (MaterialAppdelegates).
Java ZonedDateTime remains richer for some server use cases.
Regular expressions
- Dart
RegExplives indart:core. - Mind multi-line / unicode / anchoring differences vs Java
Pattern—always add tests for tricky inputs.
URIs, networking, encoding
- Prefer
Uri.parse/Uri.httpsover string concatenation—similar discipline asjava.net.URI. - HTTP clients:
http(minimal) vsdio(interceptors, cancel tokens, multipart ergonomics).
Binary data: typed_data vs NIO
| Dart | Java |
|---|---|
Uint8List |
byte[], ByteBuffer
|
ByteData |
primitive get/put on ByteBuffer
|
Endian |
ByteOrder |
Isolates copy messages; no shared **ByteBuffer between threads** like Java.
Reflection, codegen, and why Flutter differs
-
dart:mirrorsexists for VM scenarios; Flutter iOS/Android release builds do not support mirrors in typical configurations (tree shaking + AOT). - Java: reflection ubiquitous (modulo JPMS).
build_runner ≈ “annotation processing” culture: The JVM has compile-time annotation processors (APT) wired through javac / Gradle / Maven (-processor, annotationProcessor dependencies). Dart has no separate javac-style processor hook in the language itself—instead, packages such as build, source_gen, and build_runner implement a declarative, file-generating build graph. You put dev_dependency: build_runner and dev_dependency: <generator> in pubspec.yaml, annotate code (e.g. @JsonSerializable()), then run dart run build_runner build (or watch). That workflow is the standard replacement for Java’s Lombok / MapStruct / Dagger / Room–style codegen when you need *.g.dart, *.freezed.dart, mocks, serializers, etc.
Practical rule: if you type import 'dart:mirrors' in app code, stop—reach for build_runner-driven generators (json_serializable, freezed, drift, injectable, mockito codegen, …), the same way you’d reach for annotation processors in a Java service.
Annotations & metadata
Built-ins include @override, @Deprecated. Create const annotation classes — runtime reflection is limited compared to Java’s Annotation reflection APIs; codegen fills the gap. Custom annotations in Dart are often inputs to build_runner generators (same role as Java annotation processor arguments), not something you reflectively query at runtime like getDeclaredAnnotations() on the JVM.
JSON / immutable data classes
| Approach | Dart |
|---|---|
| Schema-first codegen |
json_serializable, OpenAPI generators in ecosystem |
| Algebraic / union types |
freezed (sum types + copyWith) |
| Hand-written | OK for tiny DTOs |
Local databases & ORM-ish (Flutter/mobile/server vary)
| Package | Sketch | Caveat |
|---|---|---|
| sqflite | SQLite on some platforms | Not web |
| drift | Typed SQL + migrations + codegen | Strong for structured data |
| isar | Fast embedded NoSQL | Check platform matrix |
| hive | Key-value boxes | Simplicity vs relational features |
Server-side Postgres often pairs with postgres driver + framework migrations (e.g. Serverpod).
Dependency injection & global state
| Java mindset | Dart / Flutter common |
|---|---|
| Spring singleton beans |
get_it lazy singletons |
| Constructor injection everywhere | Possible; many Flutter apps mix constructor + provider |
| Request scope |
Provider/riverpod overrides scoped to widget subtree |
Riverpod ≈ “recompute graph + caching + test overrides”. Not a servlet container.
Build systems: Maven/Gradle ↔ pub
| Concern | Java | Dart |
|---|---|---|
| Declared deps | Maven / Gradle | pubspec.yaml |
| Lockfile | Gradle locks |
pubspec.lock (commit in apps, not always in pure libs) |
| Build plugins / codegen | Gradle annotationProcessor, maven-compiler-plugin processor path |
build_runner + generator packages (build, source_gen); no separate “APT” concept—tasks are dart run build_runner ...
|
| Multi-package repos | composite builds |
melos mono-repo helper |
Note: Java annotation processing is a compiler plugin convention; Dart’s parallel is build_runner orchestrating builder packages—not a keyword in the language, but the default industrial approach to generated sources.
Testing landscape (beyond JUnit)
-
Unit:
package:test,group/setUp/tearDownfeel like JUnit rules-lite. -
Widget:
flutter_test+WidgetTester(think UI unit with fake async). - Goldens: screenshot diffing for widgets.
-
Integration / E2E:
integration_testdriving real devices/emulators.
Mocks: mockito (codegen) or mocktail (lambda-friendly).
Package catalog (extended, still incomplete)
| Area | Packages (examples) |
|---|---|
| Routing |
go_router, auto_route, beamer
|
| Networking |
http, dio, retrofit
|
| Serialization |
json_serializable, freezed
|
| Immutability helpers |
freezed, equatable
|
| Assets / env | flutter_dotenv |
| Firebase |
firebase_core, firebase_auth, cloud_firestore, … |
| Crash / perf |
sentry, firebase_crashlytics
|
| Images |
cached_network_image, flutter_svg
|
| Codegen infra |
build, build_runner, source_gen
|
| Lint |
lints, flutter_lints, very_good_analysis
|
| Monorepo | melos |
Always verify platform support (iOS/Android/Web/Desktop/Server) before you commit.
Expanded feature matrix — language surface area
| Topic | Java | Dart | Practical delta |
|---|---|---|---|
| Primitive vs object types | primitives + wrappers | unified num/int/double/bool
|
fewer autoboxing surprises; int VM 64-bit, BigInt for bigger; JS has 53-bit safe int caveats |
| Fixed arrays | String[] |
List<String> |
growable lists are default; List.filled etc. for fixed |
| Wildcards at call sites |
? extends T puzzles |
different generic puzzles | Read Dart variance docs before “clever” APIs |
| Checked exceptions | throws IOException |
none | encode failures in Result types (dartz), Either, or typed exceptions by convention |
protected |
yes | no | design smaller libraries + _ private helpers |
| Inner classes | common | supported, less common | prefer top-level private classes |
| Static import | import static |
import '...' show foo; |
top-level functions feel “static-like” |
enum with data |
recent Java upgrades | enhanced enums (fields, methods) | both strong—syntax differs |
switch exhaustiveness |
sealed helps Java |
sealed in library helps Dart |
library locality differs |
| Reflection | core to many frameworks | VM-only mirrors | codegen culture |
| Web as target | not JVM | dart2js / wasm |
conditional imports for dart:html vs dart:io
|
| Money / decimals | BigDecimal |
packages (decimal, etc.) |
plan precision early |
Soundness footguns (avoid!)
| Escape hatch | Risk |
|---|---|
dynamic |
disables static checks — like Object + unchecked casts everywhere |
Postfix ! |
NullPointerException-class failures when wrong |
covariant |
advanced param substitution — reserve for framework authors |
dart:mirrors in Flutter |
broken / disallowed in release paths |
Further reading
- Official: https://dart.dev — language tour, evolution, null safety.
- Flutter: https://flutter.dev
- Learn Dart in Y minutes — learnxinyminutes.com/docs/dart (runnable tour; watch for outdated “optional typing” prose; Dart 3 is sound).
- From Java to Dart codelab (archived Flutter-io mirror): codelabs.flutter-io.cn/.../from-java-to-dart
- pub.dev — check pub points, popularity, maintenance, null safety before you adopt a package.
- Serverpod / Relic — Serverpod blog & docs for full-stack Dart; Relic on pub.dev is the HTTP stack Serverpod builds on.
- Kotlin’s migration guides are a loose analogue for nullability habits — but Dart’s
?types and Flutter’s widget rules are their own discipline; don’t assume Kotlin === Dart.
Generators: sync* / async* / yield
Dart has lazy iterable generators and async* streams using yield / yield*:
| Form | Returns | Java intuition |
|---|---|---|
Iterable<T> sync*() sync* |
lazy sequence | custom Iterator with state machine |
Stream<T> async*() async* |
async sequence |
Publisher / reactive patterns (not identical) |
yield* delegates to another iterable/stream—useful for composing tree walks or parsers.
Iterable, Iterator, for-in vs Java
-
Iterable<E>in Dart is like Java’sIterable<T>— providesIterator<E> iterator. -
for (final x in xs)is sugar overiterator.moveNext(). -
Extension methods on
Iterable(map,where,fold…) resemble Stream API ergonomics butIterableis synchronous.
Key distinction: Java Stream is often one-shot; Dart Iterable can be re-iterated if the underlying iterable supports it (and some are single-use like Iterator patterns—read docs per concrete type).
Deferred & conditional imports
Deferred
import 'heavy.dart' deferred as heavy; — loads library later; await heavy.loadLibrary() before use. Roughly dynamic plugin loading (not OS .so like JNI—Dart bundle loading).
Conditional
Split VM vs web implementations:
import 'stub.dart'
if (dart.library.io) 'io_impl.dart'
if (dart.library.html) 'html_impl.dart';
Use when platform APIs diverge (dart:io vs dart:html)—think separate *-jvm / *-android artifacts in Maven but resolved at compile time per target.
dart:ffi vs JNI (sketch)
| Concern | JNI (Java) | dart:ffi |
|---|---|---|
| Interop target | JVM ↔ native (often C/C++) | Dart VM ↔ C ABI |
| Memory |
GetPrimitiveArrayCritical, pinning |
Pointer, calloc, Struct packing |
| Async | blocking calls off critical path | isolate-friendly; don’t block UI |
| Flutter | JNI / platform channels | FFI for perf-sensitive native; platform channels for Android/iOS SDK |
Panama in modern Java is spiritually closer ergonomically than legacy JNI, but Dart dart:ffi is still its own API surface.
Super parameters & initializer lists
Dart allows forwarding constructor parameters to super constructors compactly (super parameters), and : initializer lists run before constructor bodies—useful for final field assignment from computed values.
Think telescoping constructors in Java but with less boilerplate.
Flutter layering (Widget / Element / RenderObject)
Three trees (simplified mental model):
-
Widget— immutable configuration (“do I want padding?”). Compare to a declarative description. -
Element— life cycle of who ownsState; holds reference between frames. -
RenderObject— layout & paint; mutated during layout.
Java UI frameworks (Swing) historically emphasized mutable component graphs more; Flutter’s setState / rebuild reconciles new widget descriptions against existing elements/render objects.
Numeric types & compilation targets caveat
- On the native Dart VM,
intis a 64-bit integer (overflow wraps / is defined per operation—see language spec); useBigIntfor arbitrary-precision integer math (JavaBigIntegeranalogue). - When compiling to JavaScript, integers are limited to IEEE-754 safe integers (~±2⁵³) unless you use
BigIntpatterns appropriate for JS compilation—do not assume Javalong-range arithmetic portably without checks.
Treat numeric code that must run on Flutter Web and mobile as a cross-platform concern: tests on both targets.
Appendix: Java ↔ Dart cheat sheet (snippets)
Paired Java vs Dart examples. Prefer sound Dart 3 / null-safe snippets; remove ? only where the teaching point is nullable types.
How to read
- “Not in Java” / “Not in Dart” called out in notes.
-
Learn X in Y Minutes + From Java to Dart codelab patterns appear explicitly (Bicycle, Rectangle,
~/, cascades,map).
Entry point & top-level members
Java
public class Demo {
public static int answer = 42;
public static void main(String[] args) {
System.out.println(answer + " " + args.length);
}
}
Dart — top-level main, variables, functions (no class wrapper required):
int answer = 42;
void main(List<String> args) {
print('$answer ${args.length}');
}
Comments
Java — //, /* */, /** */ Javadoc.
Dart — same + /// doc comments drive dartdoc (like Javadoc for API extraction).
/// One-line doc.
// runtime comment
/*
block
*/
Variables: var, final, const, late
Java
final String name = "Ann";
var count = 1; // Java 10+
Dart
var count = 1; // inferred as int; cannot later assign bool
final name = 'Ann'; // single assignment, inferred String
const pi = 3.14; // compile-time constant
late String description; // non-nullable, assigned before use
Nullable types (?) — Kotlin-like, not Java primitives
Java (modern style / annotations)
@Nullable String nick;
String s = nick; // still needs null check for safety
Dart
String? nick;
String s = nick!; // bang — avoid; prefer ?. ?? or flow checks
Common operators — arithmetic and “not like Java”
Full list: Appendix — Dart operators (complete predefined set, Java notes)
Java
int a = 7 / 3; // 2 (int division)
double x = 7 / 3.0;
Dart
var q = 7 / 3; // 2.5 — because / always promotes to double when needed
var t = 7 ~/ 3; // 2 — truncating integer division (Learn X in Y Minutes)
Java — increment
for (int i = 0; i < n; i++) {}
Dart
for (var i = 0; i < n; i++) {}
Null-aware and cascades (no direct Java equivalent)
Dart only (style from null-aware history — see also Learn X in Y Minutes):
String? name;
var len = name?.length;
var display = name ?? 'guest';
name ??= 'anon';
cascadeExample() {
final sb = StringBuffer()
..write('a')
..write('b'); // cascade: same receiver
}
Java (verbose analog)
String name = null;
Integer len = name != null ? name.length() : null;
String interpolation & multiline (codelabs + Learn X in Y Minutes)
Java
String msg = "Hello, " + name + "!";
var block = """
line1
line2
""";
Dart
final msg = 'Hello, $name!'; // or ${expr}
final block = '''
line1
line2
''';
final raw = r'\n stays literal';
if / ternary
Java if (cond) {} else {}, cond ? a : b
Dart identical shape; condition must be bool (no implicit truthiness from arbitrary objects).
Loops: for, enhanced for, forEach
Java
for (int i = 0; i < xs.size(); i++) { var x = xs.get(i); }
for (var x : xs) {}
xs.forEach(x -> System.out.println(x));
Dart (codelab functional style)
for (var i = 0; i < xs.length; i++) {
final x = xs[i];
}
for (final x in xs) {}
xs.forEach(print);
// or: xs.map(scream).forEach(print);
while / do-while
Same structure in both languages.
switch — classic vs Dart 3 patterns
Java (pre–pattern matching style)
switch (k) {
case 1:
System.out.println("one");
break;
default:
break;
}
Dart — still supports break in statement switches; Dart 3 adds expression switch with patterns (no break between cases in expression form):
String label(int k) => switch (k) {
1 => 'one',
_ => 'other',
};
Dart — classic statement switch (still used; Learn X in Y Minutes style — each case falls through only with continue labels; normally end with break or return):
void demo(int v) {
switch (v) {
case 1:
print('one');
break;
default:
print('other');
}
}
Exceptions
Java — checked + unchecked.
try {
throw new IOException("x");
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
System.out.println("done");
}
Dart — no checked exceptions; throw any object.
try {
throw StateError('x');
} on StateError catch (e) {
rethrow;
} catch (e, st) {
// stack trace
} finally {
print('done');
}
Classes: fields, this constructor, no public keyword
Java Tutorial Bicycle style (multiple getters / setters)
public class Bicycle {
private int cadence;
private int speed = 0;
private int gear;
public Bicycle(int cadence, int speed, int gear) {
this.cadence = cadence;
this.speed = speed;
this.gear = gear;
}
}
Dart codelab — shorthand + _ privacy (library scope)
class Bicycle {
int cadence;
int _speed = 0;
int get speed => _speed;
int gear;
Bicycle(this.cadence, this.gear); // speed internal
void applyBrake(int decrement) => _speed -= decrement;
}
No method / constructor overloading — use named parameters
Java — overload constructors
public Rectangle() { this(0,0,0,0); }
public Rectangle(int w, int h) { this(0,0,w,h); }
Dart — single constructor with named optional + defaults (codelab Rectangle)
class Point {
final int x, y;
const Point(this.x, this.y);
}
class Rectangle {
Point origin;
int width, height;
Rectangle({this.origin = const Point(0, 0), this.width = 0, this.height = 0});
}
Factory: top-level function vs factory constructor (codelab)
Option A — Dart top-level
Shape shapeFactory(String type) {
return switch (type) {
'circle' => Circle(2),
'square' => Square(2),
_ => throw ArgumentError(type),
};
}
Option B — factory on type
abstract class Shape {
factory Shape(String type) {
return switch (type) {
'circle' => Circle(2),
'square' => Square(2),
_ => throw ArgumentError(type),
};
}
num get area;
}
Java analog is often static factory methods (Boolean.valueOf, etc.).
implements every class interface — CircleMock pattern (codelab)
Dart
abstract class Report {
String build();
}
class HtmlReport implements Report {
@override
String build() => '<html/>';
}
Java
interface Report { String build(); }
class HtmlReport implements Report {
@Override public String build() { return "<html/>"; }
}
Inheritance & super
Java
class Child extends Base {
Child(int x) { super(x); }
}
Dart
class Child extends Base {
Child(super.x);
}
Mixins — order matters (with M1, M2)
Java has single inheritance; default interface methods approximate some sharing.
Dart — mixins compose left-to-right; if two mixins supply the same member name, resolution follows Dart’s linearization (document the exact app in language spec when it matters for overrides).
class Vehicle {}
mixin Engine on Vehicle {
String sound() => 'vroom';
}
mixin Horn on Vehicle {
String beep() => 'beep';
}
class Car extends Vehicle with Engine, Horn {}
void main() {
final c = Car();
print(c.sound());
print(c.beep());
}
Design takeaway: put the “most specific” mixin last when you layer overrides—same discipline as multiple interface defaults in Java, but with mixin + super semantics.
Extension methods (Kotlin-like)
Dart
extension StrX on String {
String twice() => this + this;
}
Java — no extension methods until recent preview features; historically static helpers.
operator overloading & call
Dart
class Vec2 {
final int x, y;
const Vec2(this.x, this.y);
Vec2 operator +(Vec2 o) => Vec2(x + o.x, y + o.y);
}
class Multiplier {
final int k;
const Multiplier(this.k);
int call(int n) => k * n; // callable object
}
Java — no operator+; use plus(Vec2 o).
Generics — reification vs erasure
Java (erasure at runtime)
List<String> xs = new ArrayList<>();
// xs.getClass() is List, not List<String>
Dart
void main() {
final xs = <int>[1, 2];
print(xs.runtimeType); // List<int> — type argument preserved (reified)
}
async / await / Future / Stream
Java (CompletableFuture)
CompletableFuture.supplyAsync(() -> 1)
.thenApply(x -> x + 1)
.join();
Dart
Future<int> load() async {
final x = await Future.value(1);
return x + 1;
}
Generators: sync* / async*
Dart (Learn X in Y Minutes style)
Iterable<int> countUp(int n) sync* {
for (var i = 0; i < n; i++) {
yield i;
}
}
Stream<int> tick() async* {
for (var i = 0; i < 3; i++) {
await Future.delayed(Duration(milliseconds: 100));
yield i;
}
}
Java — no direct language feature; use Stream.iterate / reactive APIs.
Isolates (not OS threads)
Dart
import 'dart:isolate';
Future<int> heavy(int n) async {
return Isolate.run(() {
// no shared mutable heap with spawner — message-passing / copying
var s = 0;
for (var i = 0; i < n; i++) s += i;
return s;
});
}
typedef & function types
Dart
typedef IntPred = bool Function(int x);
typedef StringMap = Map<String, Object?>;
bool positive(int x) => x > 0;
void main() {
final IntPred p = positive;
print(p(1));
}
Java — IntPredicate, Predicate<Integer>, etc.
Imports: dart: / relative / package: / show / hide / as / deferred
Dart
import 'dart:convert';
import 'dart:math' as math;
import 'package:path/path.dart' show join;
import 'helper.dart' hide Secret;
import 'slow.dart' deferred as slow;
Future<void> later() async {
await slow.loadLibrary();
slow.doWork();
}
Java — import static, module path; no deferred import analog in the language proper.
Enhanced enum (Dart 2.17+)
Dart
enum HttpCode {
ok(200),
notFound(404);
final int code;
const HttpCode(this.code);
}
Java — enums strong since Java 5; enhanced with fields/methods similarly.
Records & patterns (Dart 3) / Java 16+ record
Java 21-style
record Point(int x, int y) {}
Dart 3
void main() {
final r = (1, 2); // Record type
final (a, b) = r; // destructure
print('$a $b');
}
JSON / immutable unions — freezed (pub.dev)
No single Java built-in; think Lombok + Jackson patterns. freezed generates immutable data classes, copyWith, JSON serialization (with json_serializable), and union “sealed” types—add build_runner and run:
dart run build_runner build --delete-conflicting-outputs
See freezed README for the exact annotation syntax (it evolves between major versions).
assert / debug* compilation
Dart
void check(int x) {
assert(x >= 0, 'must be non-negative');
}
Stripped in release Flutter/AOT builds by default for assert.
Cascade revisit (codelab “method cascades”)
Dart — “everything is a builder” (mutable configuration fluently)
void fill(StringBuffer sb) {
sb
..write('a')
..write('b');
}
Java — typically StringBuilder chained calls return this manually.
Dart operators — complete predefined set (Java notes)
Official reference and precedence: Operators | dart.dev. Many symbols can also be implemented as class members (user-defined operator), similar in spirit to C++/C#—Java does not allow user-defined +, [], etc.
Precedence (highest → lowest) — reproduced from the language docs; each row is higher than rows below it.
| Description (Dart) | Operators | Associativity |
|---|---|---|
| Unary postfix |
++ -- () [] ?[] . ?. !
|
None |
| Unary prefix |
-expr !expr ~expr ++expr --expr await expr
|
None |
| Multiplicative |
* / % ~/
|
Left |
| Additive |
+ -
|
Left |
| Shift |
<< >> >>>
|
Left |
| Bitwise AND | & |
Left |
| Bitwise XOR | ^ |
Left |
| Bitwise OR | `
|
` |
| Relational & type test |
>= > <= < as is is!
|
None |
| Equality |
== !=
|
None |
| Logical AND | && |
Left |
| Logical OR | `
|
|
| If-null | {% raw %}??
|
Left |
| Conditional | expr1 ? expr2 : expr3 |
Right |
| Cascade |
.. ?..
|
Left |
| Assignment |
= *= /= += -= &= ^= `
|
= %= ~/= <<= >>= >>>= ??=` |
| Spread (see note) |
... ...?
|
(not an expression operator; see below) |
Note (spread): Per dart.dev, ... / ...? are part of collection / map literals, not ordinary binary operators; “precedence” is best read as lowest in the sense that the spread operand can be any expression.
Every token — Java analogue or “Dart only”
| Dart | Role | Java analogue / comment |
|---|---|---|
() |
Call / function application |
f(a) — same; Dart supports optional/named actual args differently |
[] |
Index / subscript operator | Arrays: a[i]. List/Map: no [] on java.util.List—use get / Map#get; Dart list[i] / map[k] is operator syntax |
?[] |
Null-aware index | No single operator; list == null ? null : list[i]
|
. |
Member access |
. — same |
?. |
Null-aware member | No ?. in Java; verbose null checks or Optional
|
! (postfix) |
Not-null assertion (expr!) |
Objects.requireNonNull(expr) then use; wrong ! → runtime error (like failed cast) |
++ -- (prefix/postfix) |
Increment / decrement | Same; only on var / fields that are mutable (late / assignable) |
- (unary) |
Negation |
- — same |
! (prefix) |
Boolean NOT |
! — Java requires boolean; Dart ! expects bool in sound code |
~ (unary) |
Bitwise NOT |
~ — same for integrals |
await |
Suspend until Future completes |
CompletableFuture/virtual threads—not a unary keyword in Java the same way |
* / % |
Multiply, divide, remainder | Same; / on Dart int promotes to double where needed—unlike Java int/int
|
~/ |
Integer (truncating) division |
Math.floorDiv (Java 18+) or a / b for ints (trunc toward zero)—behavior differs from floorDiv for negatives; know your contract |
+ - (binary) |
Add / subtract | Same; + also string concat in both (prefer Dart interpolation) |
<< >> >>> |
Shifts | Java has >>>; Dart >>> is unsigned right shift (mind web 32-bit masking per docs) |
**& ^ ` |
`** | Bitwise and / xor / or |
> < >= <= |
Relational | Same |
as |
Cast |
(Type) e — Dart as throws if wrong type (also, import 'x.dart' as p uses as for a library prefix, not a cast) |
is is! |
Type test / negated test |
e instanceof T / !(e instanceof T)
|
== != |
Equality / inequality | Dart == is first.==(second) with null shortcut rules; Java == is reference compare for objects—use equals for value equality |
**&& ` |
`** | |
?? |
If-null |
Optional.ofNullable(a).orElse(b) / ternary—no ?? in Java |
??= |
Assign if null | No direct; if (x == null) x = v
|
? : |
Conditional | Same ternary |
.. ?.. |
Cascade | No cascade—repeat receiver or builder pattern |
= |
Assignment | Same |
*= /= += -= %= |
Compound assign | Same |
~/= |
Compound trunc-div | No single operator; a = Math.floorDiv(a, b) or /= for ints |
**<<= >>= >>>= &= ^= ` |
=`** | Compound bitwise/shift |
... |
Spread into another collection/map literal | Java varargs / List.copyOf / Stream.concat—no literal spread until you build lists |
...? |
Null-aware spread | Same as spread after null filter |
operator … in a class |
User-defined +, -, [], … |
Java has no user-defined operators (always methods like plus, get) |
Tiny examples — Dart-only / confusing vs Java
// ~/ vs /
assert(5 / 2 == 2.5);
assert(5 ~/ 2 == 2);
// == calls Object== / overridden ==
final s1 = 'a';
final s2 = String.fromCharCode(97);
assert(s1 == s2); // true — content equality for String
// is / as
Object o = 1;
if (o is int) print(o + 1); // promoted to int
final x = o as int;
// Null-aware and assert-non-null
String? n;
print(n?.length);
print(n!.length); // runtime if n is null
// Cascade (not nested-dot default)
final sb = StringBuffer()..write('a')..write('b');
// Spread in literal
final inner = [2, 3];
List<int>? tail;
final outer = [1, ...inner, if (true) 4, ...?tail];
Java (contrast for == on reference types):
String a = new String("x");
String b = new String("x");
System.out.println(a == b); // false — identity
System.out.println(a.equals(b)); // true — value
End of appendix snippets — see main chapters for variance, isolates detail, and tooling.
Top comments (0)