DEV Community

Cover image for Dart for Java Developers
Nayden Gochev
Nayden Gochev

Posted on

Dart for Java Developers

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.dart mixes 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

  1. Mental model in one page
  2. Why Dart feels “Java-like” (and where it diverges)
  3. Crash-course synthesis (Learn X in Y Minutes and From Java to Dart codelab)
  4. Timeline: Dart history & language evolution
  5. Syntax & structure
  6. Type system & null safety
  7. Functions, methods, parameters
  8. Classes, constructors, factories
  9. Inheritance, interfaces, mixins, extensions
  10. Generics & variance (Java vs Dart)
  11. Enums, sealed classes, patterns (Dart 2.17–3.x)
  12. Collections & iteration
  13. Errors & exceptions
  14. Asynchrony: Future, Stream, async/await, isolates
  15. Modules, libraries, part, export, packages
  16. Tooling: pub, analyzer, formatter, tests
  17. Compilation targets & runtime (vs JVM)
  18. Flutter (what Java devs need first)
  19. Backend on Dart: Shelf, Relic, Serverpod, Dart Frog, gRPC
  20. Common / notable libraries (selected)
  21. Feature matrix: Java ↔ Dart
  22. What exists in one language but not the other
  23. Migration heuristics
  24. Glossary: Java ↔ Dart terminology
  25. Keywords quick map
  26. Operators (selected) with Java notes
  27. dart:* libraries — Java mental anchors
  28. Class modifiers (Dart 3)
  29. Patterns, destructuring, switch expressions
  30. Callable objects & operator overloading
  31. typedef and type aliases
  32. Dates, time zones, internationalization (intl)
  33. Regular expressions
  34. URIs, networking, encoding
  35. Binary data: typed_data vs NIO
  36. Reflection, codegen, and why Flutter differs
  37. Annotations & metadata
  38. JSON / immutable data classes
  39. Local databases & ORM-ish
  40. Dependency injection & global state
  41. Build systems: Maven/Gradle ↔ pub
  42. Testing landscape (beyond JUnit)
  43. Package catalog (extended)
  44. Expanded feature matrix
  45. Soundness footguns
  46. Further reading
  47. Generators: sync* / async* / yield
  48. Iterable, Iterator, for-in vs Java
  49. Deferred & conditional imports
  50. dart:ffi vs JNI (sketch)
  51. Super parameters & initializer lists
  52. Flutter layering (Widget / Element / RenderObject)
  53. Numeric types & compilation targets caveat
  54. 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 / final vs Java’s var since 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.dart tour: comments, var/final/const, nested functions, Iterable/List/Map shapes ((), [], {}), 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: main at top level, new optional, this.field constructor shorthand, privacy via _, getters, no constructor overloading replaced by named parameters / defaults, factory constructors, implements without interface keyword, map/forEach functional 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 dynamic escape 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");
  }
}
Enter fullscreen mode Exit fullscreen mode

Dart

void main() {
  print('Hello');
}
Enter fullscreen mode Exit fullscreen mode

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/bool are types with fixed-size semantics but also objects in the unified type model; num is a supertype of int and double.

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)

  • String cannot hold null.
  • String? can hold null.
  • 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

  • dynamic opts out of static checks (similar in spirit to very loose typing; unlike using Object with 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) {}
Enter fullscreen mode Exit fullscreen mode

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) { }
Enter fullscreen mode Exit fullscreen mode

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'),
);
Enter fullscreen mode Exit fullscreen mode

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._();
}
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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 {}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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 channelsno 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 (Widget trees).
  • 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:ffi for 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

  1. Stop designing overload sets — reach for named params and small types.
  2. Model absence with T?, not null everywhere + comments.
  3. Embrace async/await—avoid callback pyramids like avoiding raw CompletableFuture chains in modern Java.
  4. Learn isolates before porting “background thread does shared state” Java—rethink shared memory.
  5. Learn List/Map variance rules before performance-critical numeric code with heavy generics.
  6. Use package:lints / flutter_lints early—Dart’s analyzer is your pair programmer.
  7. Prefer immutable models (final fields, freezed where 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_genno 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, throws as 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’s interface keyword 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');
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

Closest Java analogue: type parameter bounds + functional interfaces, not a first-class typedef.


Dates, time zones, internationalization (intl)

  • DateTime handles instant vs local/utc flags; time zones usually need timezone package or careful UTC storage.
  • package:intl for DateFormat, messages, plural rules — pair with Flutter localization (MaterialApp delegates).

Java ZonedDateTime remains richer for some server use cases.


Regular expressions

  • Dart RegExp lives in dart:core.
  • Mind multi-line / unicode / anchoring differences vs Java Pattern—always add tests for tricky inputs.

URIs, networking, encoding

  • Prefer Uri.parse / Uri.https over string concatenation—similar discipline as java.net.URI.
  • HTTP clients: http (minimal) vs dio (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:mirrors exists 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/tearDown feel like JUnit rules-lite.
  • Widget: flutter_test + WidgetTester (think UI unit with fake async).
  • Goldens: screenshot diffing for widgets.
  • Integration / E2E: integration_test driving 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 minuteslearnxinyminutes.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’s Iterable<T> — provides Iterator<E> iterator.
  • for (final x in xs) is sugar over iterator.moveNext().
  • Extension methods on Iterable (map, where, fold…) resemble Stream API ergonomics but Iterable is 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';
Enter fullscreen mode Exit fullscreen mode

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):

  1. Widget — immutable configuration (“do I want padding?”). Compare to a declarative description.
  2. Element — life cycle of who owns State; holds reference between frames.
  3. 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, int is a 64-bit integer (overflow wraps / is defined per operation—see language spec); use BigInt for arbitrary-precision integer math (Java BigInteger analogue).
  • When compiling to JavaScript, integers are limited to IEEE-754 safe integers (~±2⁵³) unless you use BigInt patterns appropriate for JS compilation—do not assume Java long-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);
  }
}
Enter fullscreen mode Exit fullscreen mode

Darttop-level main, variables, functions (no class wrapper required):

int answer = 42;

void main(List<String> args) {
  print('$answer ${args.length}');
}
Enter fullscreen mode Exit fullscreen mode

Comments

Java//, /* */, /** */ Javadoc.

Dart — same + /// doc comments drive dartdoc (like Javadoc for API extraction).

/// One-line doc.
// runtime comment
/*
 block
*/
Enter fullscreen mode Exit fullscreen mode

Variables: var, final, const, late

Java

final String name = "Ann";
var count = 1; // Java 10+
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Nullable types (?) — Kotlin-like, not Java primitives

Java (modern style / annotations)

@Nullable String nick;
String s = nick; // still needs null check for safety
Enter fullscreen mode Exit fullscreen mode

Dart

String? nick;
String s = nick!; // bang — avoid; prefer ?. ?? or flow checks
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

Java — increment

for (int i = 0; i < n; i++) {}
Enter fullscreen mode Exit fullscreen mode

Dart

for (var i = 0; i < n; i++) {}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

Java (verbose analog)

String name = null;
Integer len = name != null ? name.length() : null;
Enter fullscreen mode Exit fullscreen mode

String interpolation & multiline (codelabs + Learn X in Y Minutes)

Java

String msg = "Hello, " + name + "!";
var block = """
    line1
    line2
    """;
Enter fullscreen mode Exit fullscreen mode

Dart

final msg = 'Hello, $name!'; // or ${expr}
final block = '''
line1
line2
''';
final raw = r'\n stays literal';
Enter fullscreen mode Exit fullscreen mode

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));
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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',
};
Enter fullscreen mode Exit fullscreen mode

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');
  }
}
Enter fullscreen mode Exit fullscreen mode

Exceptions

Java — checked + unchecked.

try {
  throw new IOException("x");
} catch (IOException e) {
  throw new RuntimeException(e);
} finally {
  System.out.println("done");
}
Enter fullscreen mode Exit fullscreen mode

Dart — no checked exceptions; throw any object.

try {
  throw StateError('x');
} on StateError catch (e) {
  rethrow;
} catch (e, st) {
  // stack trace
} finally {
  print('done');
}
Enter fullscreen mode Exit fullscreen mode

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;
  }
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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); }
Enter fullscreen mode Exit fullscreen mode

Dartsingle 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});
}
Enter fullscreen mode Exit fullscreen mode

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),
  };
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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/>';
}
Enter fullscreen mode Exit fullscreen mode

Java

interface Report { String build(); }
class HtmlReport implements Report {
  @Override public String build() { return "<html/>"; }
}
Enter fullscreen mode Exit fullscreen mode

Inheritance & super

Java

class Child extends Base {
  Child(int x) { super(x); }
}
Enter fullscreen mode Exit fullscreen mode

Dart

class Child extends Base {
  Child(super.x);
}
Enter fullscreen mode Exit fullscreen mode

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());
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

Javano 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
}
Enter fullscreen mode Exit fullscreen mode

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>
Enter fullscreen mode Exit fullscreen mode

Dart

void main() {
  final xs = <int>[1, 2];
  print(xs.runtimeType); // List<int> — type argument preserved (reified)
}
Enter fullscreen mode Exit fullscreen mode

async / await / Future / Stream

Java (CompletableFuture)

CompletableFuture.supplyAsync(() -> 1)
  .thenApply(x -> x + 1)
  .join();
Enter fullscreen mode Exit fullscreen mode

Dart

Future<int> load() async {
  final x = await Future.value(1);
  return x + 1;
}
Enter fullscreen mode Exit fullscreen mode

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;
  }
}
Enter fullscreen mode Exit fullscreen mode

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;
  });
}
Enter fullscreen mode Exit fullscreen mode

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));
}
Enter fullscreen mode Exit fullscreen mode

JavaIntPredicate, 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();
}
Enter fullscreen mode Exit fullscreen mode

Javaimport 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);
}
Enter fullscreen mode Exit fullscreen mode

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) {}
Enter fullscreen mode Exit fullscreen mode

Dart 3

void main() {
  final r = (1, 2); // Record type
  final (a, b) = r;   // destructure
  print('$a $b');
}
Enter fullscreen mode Exit fullscreen mode

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');
}
Enter fullscreen mode Exit fullscreen mode

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');
}
Enter fullscreen mode Exit fullscreen mode

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];
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

End of appendix snippets — see main chapters for variance, isolates detail, and tooling.

Top comments (0)