18 · Modules
Java with extern
Java 8 and later, live objects and Maven libraries through the JVM provider.
On this page
Import java from the jvm plugin. Function signatures use Zolo types, while
the bodies use Java. Call the functions directly, with the same syntax as other
Zolo functions. These examples run with the CLI and an installed JVM plugin;
the browser Playground cannot host a JVM.
Use a leading Java import, return an exact integer and await a CompletableFuture.
use plugin jvm::{java}
extern java fn upper(value: str) -> str {
import java.util.Locale;
return value.toUpperCase(Locale.ROOT);
}
extern java fn exact(value: int) -> int {
return value;
}
extern java async fn later(value: int) -> int {
import java.util.concurrent.CompletableFuture;
return CompletableFuture.supplyAsync(() -> value + 1L);
}
print(upper("hello Java"))
print(exact(9007199254740993))
print(await later(41))
Requires the Zolo CLI/host — open in the playground or run locally.
HELLO JAVA
9007199254740993
42
int and i64 map to Java long, preserving signed 64-bit integers, including
values above 2^53. i32 maps to Java int; narrower primitives keep their native
types. Use 1L for a Java long literal. Overloads follow the declared types,
and conversions that overflow or lose precision report an error.
extern java async fn adapts a Java CompletionStage<T> to a Zolo Future.
Await it normally. Java functional interfaces can receive Zolo callbacks;
callbacks from Java threads return to the owning Zolo runtime. Cancelling a
Future requests cancellation, but Java work that ignores interruption can
continue running.
Files, JARs and objects
Import the public Person class from a Java file through a provider alias. Construct it with .new, call methods, update a field and pass the same live instance to a separate inline function.
use plugin jvm::{java as j}
extern j from "./java_fixtures/Person.java" use {Person}
extern j fn same(person: Person) -> Person { return person; }
let person = Person.new(name: "João", age: 30)
print(person.getName())
print(person.getAge())
person.age = 31
print(person.age)
print(Person.twice(21))
print(person == same(person))
Requires the Zolo CLI/host — open in the playground or run locally.
João
30
31
42
true
Paths follow the importing Zolo file. The companion
java_fixtures/Person.java exposes public constructors and methods, plus a
writable public age field. Objects keep their native identity and state;
passing an object between these bindings does not copy its fields.
A local JAR uses the same import form:
extern java from "./Person.jar" use {Person}. With a JAR exposing the same
public API, let person = Person.new("João", 30) followed by
print(person.getName()) prints João. Use positional arguments for JAR
bindings when parameter names are unavailable. Source imports expose parameter
names for named calls.
JARs containing the MethodParameters attribute, normally produced by
javac -parameters, also retain those names: the same class accepts
Person.new(name: "João", age: 30).
Use public Java APIs across independently compiled modules. Package-private access requires sources compiled together. For varargs, open generics or an unsupported native shape, expose a small Java wrapper with a concrete signature. Static fields also need a wrapper in this version.
Maven libraries
Declare each dependency once at file scope. Java functions keep their normal imports, and Maven resolves the transitive JAR graph before compilation.
Pin Guava with its Maven group:artifact coordinate and use Joiner from Java.
// Prepare with `zolo install` or opt into `zolo run ... --install=auto`.
use plugin jvm::{java}
extern java deps {
"com.google.guava:guava" = "33.4.0-jre"
}
extern java fn join(values: [str]) -> str {
import com.google.common.base.Joiner;
return Joiner.on(", ").join(values);
}
print(join(["Java", "Maven", "Zolo"]))
Requires the Zolo CLI/host — open in the playground or run locally.
Java, Maven, Zolo
For a standalone script named analysis.zolo, allow preparation on the first
run with zolo run analysis.zolo --install=auto. To install explicitly:
zolo install analysis.zolo
zolo run analysis.zolo --install=manual
zolo run analysis.zolo --offline --frozen
Standalone scripts default to auto; projects default to manual. The script
lock is analysis.zolo.lock. --offline uses local caches only, while --frozen
requires a matching lock and prevents changes to it. Opening a file in the
editor does not download dependencies or execute authored Java code.
For a project, move the dependency declaration to zolo.toml and keep the Java
function and its import in main.zolo:
[package]
name = "java-demo"
version = "0.1.0"
[packages]
schema = 2
[foreign]
install = "manual"
[foreign.jvm.dependencies]
"com.google.guava:guava" = "33.4.0-jre"
Run zolo install main.zolo, then zolo run main.zolo --offline --frozen.
The project uses zolo.lock. Identical source and manifest declarations merge;
conflicting requirements report their origins. Direct Maven declarations accept
exact versions or native version intervals. Direct BOM, classifier, scope and
exclusion fields are not exposed yet.
Choose Java and deploy
Source compilation and semantic editor features require a JDK 8 or later.
Set ZOLO_JAVA_HOME before the first Java call; JAVA_HOME is a fallback.
The JVM architecture must match Zolo. Java runtimes are installed separately.
Authored source targets Java 8 by default, even with a newer JDK. Set
ZOLO_JAVA_RELEASE, for example to 17, to use a newer language/API target;
the selected compiler must support that target.
VM, native and LLVM execute the same provider. Compiled artifacts retain class files, dependency JARs, resources and source maps. Dynamic, embedded and static plugin packaging can run without the original sources or a Java compiler. Deployment still needs a JRE compatible with the selected target and every dependency; packaging the plugin does not bundle Java. Libraries using native binaries retain their platform requirements. Hosted JVM execution is unavailable on wasm-aot.
Java exceptions work with ordinary try/catch and point to authored source.
For libraries that use an existing common pool or another executor, propagate
the session context class loader or pass it explicitly to resource/service
lookups. See the JVM plugin guide for these integration details. Acceptance
currently covers Windows x64 with JDK 8, 11 and 23, plus compiler-free JRE 8
deployment; Linux and macOS still need runtime validation.
See also