← SuperJ Manual

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 (vtable + type_id).
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.
Labeled break/continueComplexityRestructure with a flag, or extract a method.
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;   // compile error: buf would escape its arena
    }                    // temp dropped: buf freed, n still valid
}

A reference that would outlive its arena is a compile error, not a use-after-free. There is no destroy()/finalize() — arena drop is the reclamation mechanism. See Memory Safety.

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

Layout: { i32 length, i32 elem_size, i8 data[] } — an 8-byte header 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 vtable).

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

struct — fixed-layout wire types

A struct is a reference type with a known byte layout — no vtable, no inheritance, no interfaces. Designed for wire formats and zero-copy protocol code. Full spec: doc/struct.md (shipped with the SDK sources).

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
}

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). Full list: SIMD.md (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, 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
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
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 … Main./target/debug/<name>native binary

6. Reserved keywords

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

New vs Java: arena (scoped memory), local (stack allocation), i128/i256/i512 (wide ints), 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