Baike.dev
All toolsOnline toolsAI codingTrendingOpen sourceNewsSubmit
Log in
< Back to tools
J

j4rs

> 编程语言
Open source

Java for Rust

750 stars0 likes0 views
WebsiteGitHub

About

Java for Rust

j4rs stands for 'Java for Rust' and allows effortless calls to Java code from Rust and vice-versa.

Features

  • Rust to Java direction support (call Java from Rust).
    • No special configuration needed (no need to tweak LD_LIBRARY_PATH, PATH etc).
    • Easily instantiate and invoke Java classes.
    • Support custom types via serialization.
    • .async/.await support
    • Casting support.
    • Java arrays / variadic support.
    • Java generics support.
    • Java primitives support.
    • Java instances invocations chaining.
    • Java -> Rust callbacks support.
    • Simple Maven artifacts download and deployment.
  • Java -> Rust support (Call Rust from Java).
  • JavaFX support (including FXML support).
  • Tested on Linux, Windows and Android.

Instead of donating to the dev

With the hope that you find this project useful, please consider donating to charity.

Usage

Basics

…

Instancess of Java Lists and Maps can be created with the java_list and java_map functions:

rust
let rust_vec = vec!["arg1", "arg2", "arg3", "arg33"];

// Generate a Java List. The Java List implementation is the one that is returned by java.util.Arrays#asList
let java_list_instance = jvm.java_list(
    JavaClass::String,
    rust_vec)?;

let rust_map = HashMap::from([
    ("Potatoes", 3),
    ("Tomatoes", 33),
    ("Carrotoes", 333),
]);

// Generate a java.util.HashMap.
let java_map_instance = jvm.java_map(
    JavaClass::String,
    JavaClass::Integer,
    rust_map)?;

Passing arguments from Rust to Java

j4rs uses the InvocationArg enum to pass arguments to the Java world.

Users can benefit of the existing TryFrom implementations for several basic types:

…

And for Vecs:

rust
let my_vec: Vec = vec![
    "abc".to_owned(),
    "def".to_owned(),
    "ghi".to_owned()];

// Creates List
let i10 = InvocationArg::try_from(my_vec.as_slice())?;

let another_vec: Vec = vec![0.0, 1.0, 3.6];

// Creates List
let i11 = InvocationArg::try_from(another_vec.as_slice())?;

The j4rs apis accept InvocationArgs either as references, or values:

rust
let inv_args = InvocationArg::try_from("arg from Rust")?;
let _ = jvm.create_instance("java.lang.String", &[&inv_args])?; // Pass a reference
let _ = jvm.create_instance("java.lang.String", &[inv_args])?;  // Move

The Instances returned by j4rs can be transformed to InvocationArgs and be further used for invoking methods as well:

rust
let one_more_string_instance = jvm.create_instance(
  "java.lang.String",     // The Java class to create an instance for
  InvocationArg::empty(), // The `InvocationArg`s to use for the constructor call - empty for this example
)?;

let i11 = InvocationArg::try_from(one_more_string_instance)?;

To create an InvocationArg that represents a null Java value, use the From implementation with the Null struct:

rust
let null_string = InvocationArg::from(Null::String);                // A null String
let null_integer = InvocationArg::from(Null::Integer);              // A null Integer
let null_obj = InvocationArg::from(Null::Of("java.util.List"));     // A null object of any other class. E.g. List

Passing custom arguments from Rust to Java

Custom types, for which there is no TryFrom implementation, are also supported via serialization.

To use a custom struct MyBean as an InvocationArg it needs to be serializable:

rust
#[derive(Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
struct MyBean {
    someString: String,
    someInteger: isize,
}

Then, an InvocationArg can be created like:

rust
let my_bean = MyBean {
    someString: "My String In A Bean".to_string(),
    someInteger: 33,
};
let ia = InvocationArg::new(&my_bean, "org.astonbitecode.j4rs.tests.MyBean");

And it can be used as an argument to a Java method that accepts org.astonbitecode.j4rs.tests.MyBean instances.

Of course, there should exist a respective Java class in the classpath for the deserialization to work and the custom Java Object to be created:

java
package org.astonbitecode.j4rs.tests;

public class MyBean {
    private String someString;
    private Integer someInteger;

    public MyBean() {
    }

    public String getSomeString() {
        return someString;
    }

    public void setSomeString(String someString) {
        this.someString = someString;
    }

    public Integer getSomeInteger() {
        return someInteger;
    }

    public void setSomeInteger(Integer someInteger) {
        this.someInteger = someInteger;
    }
}

Async support

(v0.16.0 onwards)

j4rs supports .async/.await viaJvm::invoke_async function. The function returns a Future, which is completed via the Receiver of a oneshot channel.

In Java side, the methods that can be invoked by invoke_async, must return a Java Future. When the Java Future completes, the Java side of j4rs invokes native Rust code that completes the pending Rust Future with either success or failure, using the Sender of the oneshot channel that was created when the invoke_async was called.

For example, assuming we have a Java method that returns a Future:

java
package org.astonbitecode.j4rs.tests;

public class MyTest {
  private static ExecutorService executor = Executors.newSingleThreadExecutor();

  // Just return the passed String in a Future
  public Future getStringWithFuture(String string) {
    CompletableFuture completableFuture = new CompletableFuture<>();
    executor.submit(() -> {
      completableFuture.complete(string);
      return null;
    });
    return completableFuture;
  }
}

We can invoke it like following:

rust
let s_test = "j4rs_rust";
let my_test = jvm.create_instance("org.astonbitecode.j4rs.tests.MyTest", InvocationArg::empty())?;
let instance = jvm.invoke_async(&my_test, "getStringWithFuture", &[InvocationArg::try_from(s_test)?]).await?;
let string: String = jvm.to_rust(instance)?;
assert_eq!(s_test, string);

Please note that it is better for the Java methods that are invoked by the invoke_async function to return a CompletableFuture, as this improves performance.

j4rs handles simple Java Futures that are not CompletableFutures with polling, using an internal one-threaded ScheduledExecutorService.

This has apparent performance issues.

invoke_async and Send

Instances are Send and can be safely sent to other threads. However, because of Send Approximation, the Future returned by invoke_async is not Send, even if it just contains an Instance. This is because the Jvm is being captured by the async call as well and the Jvm is not Send.

In order to have a Future that is Send, the Jvm::invoke_into_sendable_async can be used. This function does not get a Jvm as argument; it creates one internally when needed and applies some scoping workarounds in order to achieve returning a Future which is also Send.

Discussion here.

Casting

An Instance may be casted to some other Class:

rust
let instantiation_args = vec![InvocationArg::try_from("Hi")?];
let instance = jvm.create_instance("java.lang.String", instantiation_args.as_ref())?;
jvm.cast(&instance, "java.lang.Object")?;

Java arrays and variadics

rust
// Create a Java array of Strings `String []`
let s1 = InvocationArg::try_from("string1")?;
let s2 = InvocationArg::try_from("string2")?;
let s3 = InvocationArg::try_from("string3")?;

let arr_instance = jvm.create_java_array("java.lang.String", &[s1, s2, s3])?;
// Invoke the Arrays.asList(...) and retrieve a java.util.List
let list_instance = jvm.invoke_static("java.util.Arrays", "asList", &[InvocationArg::from(arr_instance)])?;

When creating an array of primitives, each InvocationArg item needs to be converted to one containing primitive value.

rust
let doubles = vec![0.1, 466.5, 21.37];

// Creates Java `double` primitives within a Rust `Vec`
let double_args: Vec = doubles.iter()
    .map(|value| Ok::(
        InvocationArg::try_from(value)?.into_primitive()?
    ))
    .collect::>()?

// Creates an instance of `double []`
let doubles_array = InvocationArg::try_from(
    jvm.create_java_array("double", &double_args)?
)?;

Java Generics

rust
// Assuming the following map_instance is a Map
// we may invoke its put method
jvm.invoke(&map_instance, "put", &[InvocationArg::try_from("one")?, InvocationArg::try_from(1)?])?;

Java primitives

Even if auto boxing and unboxing is in place, j4rs cannot invoke methods with primitive int arguments using Integer instances.

For example, the following code does not work:

rust
let ia = InvocationArg::try_from(1_i32)?;
jvm.create_instance("java.lang.Integer", &[ia])?;

It throws an InstantiationException because the constructor of Integer takes a primitive int as an argument:

Exception in thread "main" org.astonbitecode.j4rs.errors.InstantiationException: Cannot create instance of java.lang.Integer at org.astonbitecode.j4rs.api.instantiation.NativeInstantiationImpl.instantiate(NativeInstantiationImpl.java:37) Caused by: java.lang.NoSuchMethodException: java.lang.Integer.(java.lang.Integer) at java.base/java.lang.Class.getConstructor0(Class.java:3349) at java.base/java.lang.Class.getConstructor(Class.java:2151) at org.astonbitecode.j4rs.api.instantiation.NativeInstantiationImpl.createInstance(NativeInstantiationImpl.java:69) at org.astonbitecode.j4rs.api.instantiation.NativeInstantiationImpl.instantiate(NativeInstantiationImpl.java:34)

In situations like this, the java.lang.Integer instance should be transformed to a primitive int first:

rust
let ia = InvocationArg::try_from(1_i32)?.into_primitive()?;
jvm.create_instance("java.lang.Integer", &[ia]);

Java instances chaining

rust
use j4rs::{Instance, InvocationArg, Jvm, JvmBuilder};

// Create a JVM
let jvm = JvmBuilder::new().build()?;

// Create an instance
let string_instance = jvm.create_instance(
  "java.lang.String",
  &[InvocationArg::try_from(" a string ")?],
)?;

// Perform chained operations on the instance
let string_size: isize = jvm.chain(string_instance)
    .invoke("trim", InvocationArg::empty())?
    .invoke("length", InvocationArg::empty())?
    .to_rust()?;

// Assert that the string was trimmed
assert!(string_size == 8);

Callback support

j4rs provides support for Java to Rust callbacks.

These callbacks come to the Rust world via Rust Channels.

In order to initialize a channel that will provide Java callback values, the Jvm::invoke_to_channel should be called. It returns a result of InstanceReceiver struct, which contains a Channel Receiver:

…

In the Java world, a Class that can do Native Callbacks must extend the org.astonbitecode.j4rs.api.invocation.NativeCallbackToRustChannelSupport

For example, consider the following Java class.

Issues· 0 open

View all issuesOpen on GitHub

No open issues yet, or sync has not completed.

> Tags

Rustjavajnirust

No comments yet. Be the first to share.

> Details

PublishedAug 1, 2026
UpdatedSep 17, 2026
Category编程语言
PricingOpen source

> Related tools

T
TypeScript
JavaScript 的超集,为前端与全栈提供静态类型
P
Python
通用编程语言,广泛用于 Web、数据与 AI
G
Go
Google 推出的简洁高效系统语言