Home · ← SuperJ Manual EN|中文

Language Reference (Java → SuperJ)

This is a reference for writers of SuperJ programs, not a tutorial. It assumes you already know Java and covers the deltas: what carries over unchanged, what's removed, what's changed, and what's new. For the ramp-up walkthrough see Getting Started; for the build tool see The Build System.

SuperJ reads like Java and behaves like C. It is not a subset of Java — it keeps the productive parts, drops the parts that fight predictable native performance, and adds systems-level capability Java has no answer for.


1. What's identical to Java

If you can write Java, most of what you write in SuperJ is the same:


2. What's removed from Java

Each removal buys predictability or removes a class of bug. The replacement, where one exists, is listed.

RemovedWhyReplacement
Threads, Thread lifecycle, Runnable thread creationSingle-threaded by design — deterministicScale with processes + shared mmap + a total-order event stream (SEDA). Runnable/SAM interfaces remain as ordinary types, just not threaded.
synchronized, volatile, wait/notify/notifyAll, java.util.concurrentNo memory model, no monitorsNone. See Concurrency.
Lambdas / closuresHidden capture machinery, unpredictable allocation:: method references — the sole first-class function syntax. Capture-free.
Autoboxing / unboxingHides allocation; no wrapper classesPrimitives are never generic type args (List<int> is a compile error). Use primitive-specialized collections (IntArrayList, Int2IntMap, …).
Reflection, ClassLoaderEverything known at compile timeStatic resolution. instanceof/casts still work at runtime.
Non-static inner classesHidden enclosing-instance referenceStatic nested classes only. Anonymous classes are allowed but do not capture locals/params/enclosing instance.
Checked exceptionsNoise; unenforced in practiceAll exceptions unchecked. throws allowed but ignored (documentation only).
finalize() / destroy() / DestroyableNo-op under arenasArena reclamation; explicit close()/free()/zeroize() for native resources. See Memory Safety.
JPMS modulesPackages onlypackage a.b.c; ⇔ directory path.
switch pattern matching / arrow labelsComplexityClassic colon form case V: only.
User-defined annotations (@interface)No consumer (no reflection, no APT)Built-in @Override/@Test/@Ignore only.

3. What's changed from Java

These features still exist but their semantics differ. This is where you trip if you're used to Java.

new is arena-scoped (no GC)

There is no tracing garbage collector. new allocates from an arena:

Scoping is lexical, not dynamic: a new belongs to the arena it's written inside. A method you call from inside an arena block still uses the global arena unless it opens its own block.

Node createNode(int v) {
    return new Node(v);          // global arena — safe to return
}

void process() {
    arena temp {
        Buffer buf = new Buffer(1024);  // in temp
        Node n = createNode(5);         // in global (createNode has no arena block)
        outer = buf;   // ok: escape analysis places buf where it outlives temp
    }                    // temp dropped: buf still valid, n still valid
}

A value that outlives its arena is placed in a region that does outlive it — the stack if it stays in the frame, the long-lived arena if it leaves. Not rejected, not a use-after-free. Escape analysis runs on every build (the --no-escape-analysis flag turns off the stack-allocation optimization, not the correctness of placement). There is no destroy()/finalize() — arena drop is the reclamation mechanism. See Memory Safety.

The drop happens on every way out of the block — falling off the end, a return from inside it, a break/continue that leaves it (labeled or unlabeled). A thrown exception is the one gap: it unwinds past the frame without dropping the arena.

String is not arena memory. Only new — objects, arrays, structs — routes to the active arena. A String body (every +, substring, valueOf, StringBuilder.toString()) is allocated separately and never reclaimed, inside an arena block or outside one. An arena block does not bound the cost of code that builds strings; reuse a StringBuilder or work in byte[] on hot paths.

local — stack allocation (developer privilege)

local declares a stack-allocated object or array (lowered to alloca):

local Point p = new Point(1, 2);   // on the stack, freed on function return

Constraints: not with primitives (local int x is an error), must have an initializer. The compiler does NOT escape-check local — a local ref that escapes its scope is undefined behavior, like a C stack pointer. Use arena blocks for the checked, safe form.

string / String is UTF-8, byte-indexed

One type (string is a keyword alias for String). Deliberately different from Java:

Java StringSuperJ String
EncodingUTF-16UTF-8 by construction
length()UTF-16 code unitsbyte count of the UTF-8 encoding
charAt(i)a 16-bit charcodepoint index; use charAt/codepoint access returning int
Backingchar[]byte[]-backed, length-prefixed, NUL-terminated for zero-copy C handoff
==reference identityreference identity (same as Java — use .equals() for content)

An embedded NUL is an ordinary byte"ab\0cd".length() == 5, and all String methods honor the stored length, not the first NUL. The one place NUL bites: the raw C-string view handed to printf("%s")/native code stops at the first NUL. True binary belongs in a byte[], not a String.

Arrays carry a runtime elem_size and are not Object

Arrays have no object header — a small length/elem-size prefix then elements. Consequences:

Generics are erased, and primitives aren't allowed as type args

T erases to Object; T extends Foo erases to Foo. Bridge methods are generated where erased signatures differ. There is no autoboxing, so List<int> is a compile error — use a primitive-specialized collection (IntArrayList, Int2IntMap, …) or int[].

Exceptions are all unchecked

try/catch/finally/throw/multi-catch/try-with-resources all work as in Java. throws on a declaration is accepted but not enforced — it's documentation. Every exception is unchecked; the compiler never makes you catch or declare one.

instanceof pattern matching with flow scoping

if (obj instanceof String s) {
    // s is in scope and typed String here, and only here
    return s.length();
}
// s is NOT in scope here

Flow scoping: the binding is in scope in the if then-branch, the while loop body, and after a guarding if (!(obj instanceof T t)) return;. The binding is final — reassigning it is a compile error. Arrays are excluded as instanceof/cast targets (no object header).

Definite assignment is enforced

A local read before it's assigned is a compile error, same as Java — but SuperJ enforces it strictly. No accidental garbage reads.


4. What's new in SuperJ (not in Java)

arena blocks — scoped memory

arena temp {
    // new allocates from temp
}   // temp dropped here — O(1), no scan, no pause

Lexical, not dynamic. The compile-time escape check makes a dangling reference a compile error. See §3 above and Memory Safety.

local — stack-scoped allocation

See §3. Developer-privilege, UB-on-escape — use arena for the checked form.

Wide integers: i128, i256, i512

First-class signed primitives, usable everywhere int/long are:

i128 mask  = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFL;
i256 big   = mask * mask;
i512 huge  = ...;
long  to   = (long) mask;        // narrowing cast

Complex numbers: cdouble, cfloat

First-class complex primitives — two floating-point values (real + imaginary) packed as one primitive type:

cdouble z  = ComplexMath.fromPair(3.0, 4.0);   // 3 + 4i
cdouble j  = 0.0 + 1.0i;                        // imaginary literal → 1i
cdouble w  = z * z;                              // (3+4i)² = -7+24i
double  r  = ComplexMath.real(z);               // 3.0
double  im = ComplexMath.imag(z);               // 4.0
double  a  = ComplexMath.abs(z);                 // 5.0
cdouble e  = ComplexMath.exp(j);                // e^i = cos(1)+i·sin(1)

Literal syntax: the i suffix denotes the imaginary unit — 3.0i is a cdouble with real=0, imag=3.0; 2.5fi is a cfloat with real=0, imag=2.5. Combine a real and imaginary literal to construct a complex value: ComplexMath.fromPair(3.0, 4.0) produces 3 + 4i.

Arithmetic semantics:

Arrays: cdouble[] and cfloat[] are ordinary arrays — each element is a contiguous real+imag pair. Use --no-bounds-check for hot numeric loops.

ComplexMath SDK class (sj.lang.ComplexMath): static methods mirroring java.lang.Math over C99 <complex.h>:

Method (cdouble)cfloat variantDescription
abs(cdouble) → doubleabsFmagnitude `\z\`
arg(cdouble) → doubleargFargument (phase angle)
conj(cdouble) → cdoubleconjFcomplex conjugate
exp(cdouble) → cdoubleexpFe^z
log(cdouble) → cdoublelogFnatural logarithm
sqrt(cdouble) → cdoublesqrtFsquare root
sin/cos/tansinF/cosF/tanFtrig
sinh/cosh/tanhsinhF/coshF/tanhFhyperbolic
pow(cdouble, cdouble)powFbase^exp
real(cdouble) → doublerealFreal component
imag(cdouble) → doubleimagFimaginary component
norm(cdouble) → doublenormF`z²` (sum of squares)
fromPair(double, double) → cdoublefromPairFconstruct from re+im

FP determinism caveat: complex * and / involve multiple floating-point operations (fused multiply-add may apply), so results are arch-dependent — do not assert bit-exact complex FP output across architectures. This mirrors the real FP caveat: use a tolerance when comparing complex results across platforms.

struct — fixed-layout wire types

A struct is a value type with a known, packed byte layout — no object header, no identity, no inheritance, no interfaces. Designed for wire formats and zero-copy protocol code. See the Struct guide for the deep dive.

struct Header {
    int    magic;
    long   timestamp;
    byte[16] uuid;          // fixed-length array field
    string payload;         // var-length: UTF-8 text in the trailing data area
}

The synthesized accessors — write and read — in one round-trip through the flyweight:

struct Order {
    long   id;          // fixed primitive
    int    qty;
    byte   side;        // 0 = buy, 1 = sell
    string symbol;      // var-length UTF-8 text
    bytes  memo;        // var-length raw bytes
}

// WRITE — bind a flyweight to a buffer and fill fields (no per-field allocation):
byte[] buf = new byte[256];
Order w = Order.view(buf, 0);
w.id = 42;
w.qty = 100;
w.side = 1;
w.symbol = "GOOG";                // string field  <- a String
w.memo   = new byte[]{7, 8, 9};   // bytes  field  <- a byte[]
int n = w.totalSize();            // Order.SIZE + 4 ("GOOG") + 3 (memo); the bytes to send

// READ — bind a fresh view to those bytes; each field reads back as its declared type:
Order r = Order.view(buf, 0);
long   id   = r.id;               // primitive  -> its value  (42)
int    qty  = r.qty;              //               (100)
String sym  = r.symbol;           // string field -> a String  ("GOOG")
byte[] memo = r.memo;             // bytes  field -> a byte[]   ({7,8,9})
// String s = new String(r.symbol);   // also works: a distinct copy

You assign each field its declared type and read it back the same way — a string field is a String on both sides, a bytes field a byte[]. Var-length fields (symbol, memo) are written in declaration order; reset() rewinds to refill the same buffer.

Rectangular arrays: int[,], int[,,]

Contiguous, stride-indexed, a distinct type from jagged int[][]:

int[,] m = new int[3, 4];     // single allocation, row-major
m[1, 2] = 7;
int total = m.length;         // 12 (product of dimensions)

First-class SIMD vectors

Accessed via the builtin Simd class — no new syntax, no new keywords. Small fixed-size arrays are the container; Simd methods operate on them as vector registers:

float[4] a = ..., b = ..., c = new float[4];
Simd.addFloat4(a, 0, b, 0, c, 0);   // c = a + b, 4 lanes at once

Vector families (element type + lane count, encoded in the method-name suffix):

WidthISAFamilies
128-bitSSE2Byte16, Short8, Int4, Long2, Float4, Double2
256-bitAVX2Byte32, Short16, Int8, Long4, Float8, Double4

i128 and boolean are not SIMD element types. Naming: Simd.operation<Family>(src, srcOff, …, dst, dstOff). Operations: arithmetic, bitwise, compare (produce mask lanes 0/-1), blend, convert (widen/narrow), reduce (sum/min/max/any/all), math (sqrt/floor/fma), gather/scatter, dot, prefix-sum, load/store (aligned + unaligned). The full operation list is in the SIMD reference (shipped with the SDK sources). --simd=sse2|avx2|none selects the target ISA (sse2 default; avx2 enables the 256-bit families).

native methods — straight to the C ABI

public class Crypto {
    public native static long hash(byte[] data, int len);
}

The systems SDK (at a glance)

Batteries included; every package is under sj.*:

PackageWhat's in it
sj.langString, StringBuilder, Math, ComplexMath, Integer, Long, Short, Byte
sj.utilArrayList, IntArrayList, LongArrayList, HashMap, Int2IntMap, Int2ObjMap, HashSet, Arrays, Hash, Random
sj.ioFile, FileReader, FileWriter, Closeable
sj.httpincremental HTTP/1.1 server, ContentType, Cookie, TLS 1.3
sj.jsonhand-written incremental JSON parser
sj.sedathe event-driven framework (Apps → Sequencer → event queue)
sj.cryptoSHA256 (ARM NEON), Secp256k1, EthereumAddress, EIP-712, TLS engine
sj.netNioAdapter event loop (TCP/UDP/Unix, epoll/kqueue), UnixGateway
sj.testAsserts, the unit-test harness

Discover the API with superj doc --list and superj doc <class-fqn>.


5. Quick syntax table

Scan for the construct you need. "Java form" is what you'd write in Java; "SuperJ form" is what you write here.

ConstructJava formSuperJ formNote
Hello worldpublic class H { public static void main(String[] a){ System.out.println("hi"); } }sameidentical
String typeStringstring or Stringstring is a keyword alias; one type
String lengths.length() (UTF-16 units)s.length() (bytes) / s.charCount() (codepoints)UTF-8
Declare a generic listList<Integer> xsArrayList<Integer> — or IntArrayListno int as type arg; no boxing
Lambda() -> doX()Thing::doXmethod refs only; target a SAM interface
Functional targetRunnable r = () -> …Runnable r = MyClass::runSAM interface, capture-free
Inner classclass Outer { class Inner {} }class Outer { static class Inner {} }static nested only
Anonymous class w/ capturenew Foo() { void m(){ use(local); } }new Foo() { void m(){ /* can't use local */ } }anonymous classes don't capture
Threadnew Thread(r).start()run a process; or a SEDA appno threads
Locksynchronized (obj) { … }none; single-threaded
Memory cleanupobj = null; (hint to GC)nothing; or arena block dropno GC; arena frees on scope exit
Scoped temp memory(no equivalent)arena temp { … }new — O(1) drop at block exit
Stack object(no equivalent)local Point p = new Point(1,2);new — UB if it escapes
128-bit intBigIntegeri128 x = …;first-class primitive
256-bit intBigIntegeri256 x = …;first-class primitive
512-bit intBigIntegeri512 x = …;new; can't cross native
Complex (double)(no equivalent)cdouble z = ComplexMath.fromPair(3.0, 4.0);new; two doubles packed as one primitive
Complex (float)(no equivalent)cfloat w = ComplexMath.fromPairF(2.0f, 3.0f);new; two floats
Imaginary literal(no equivalent)cdouble j = 1.0i;new; i suffix = imaginary unit
Wire-format record(no equivalent)struct Header { int magic; … }new; Header.view(buf, off) flyweight
2-D dense arrayint[][] gridint[,] gridnew; contiguous, stride-indexed
3-D dense arrayint[][][] cubeint[,,] cubenew
SIMD add 4 floats(no direct equiv)Simd.addFloat4(a, 0, b, 0, c, 0)new; float[4] is the container
Call into CJNI boilerplatepublic native static long hash(byte[] d, int n);new; mangled sj_… symbol
Checked exceptionthrows IOExceptionthrows IOException (accepted, not enforced)all unchecked
Try-with-resourcestry (var r = …) { … }sameresource needs a void close()
Multi-catch`catch (AB e)`same
Labeled break/continueouter: for… break outer;sameJava semantics; unwinds try-finally
instanceof patternif (x instanceof T t)sameflow scoping; arrays excluded
Test methodJUnit @Test@Test static void name() { … } + // UNIT markerbuilt-in harness
Skip a test@Ignore (JUnit)@Test @Ignore static void name() { … }built-in
File extension.java.sj
Buildjavac / mvn / gradlesuperj build / superj runsee Build System
Runjava -cp … Maintarget/<name>native binary

6. Reserved keywords

abstract   boolean    break      byte       case       catch      char
class      const      continue   cdouble   cfloat     default    do
double     else       enum       extends    final      finally    float
for        goto       if         i128       i256       i512       implements
import     int        interface  instanceof local      long       native
new        null       package    private    protected  public     return
short      static     string     struct     super      switch     synchronized
this       throw      throws     transient  try        void       volatile
while      assert     strictfp

New vs Java: arena (scoped memory), local (stack allocation), i128/i256/i512 (wide ints), cdouble/cfloat (complex numbers), string (alias for String), struct (wire-format type).

Reserved-but-unused (inherited from Java's keyword set, not implemented): const, goto, assert, strictfp, transient, synchronized, volatile. They're reserved so you can't use them as identifiers; the features they name in Java are removed (§2) or not implemented.

SuperJ — manual · generated from language-reference.md at pack time EN|中文