Skip to content

Fundamentals

The following is a list of Java conventions, and how they map to Lua.

Imports

Basic Case

Lua
local BlockState = require("net.minecraft.world.level.block.state.BlockState")
Java
import net.minecraft.world.level.block.state.BlockState;

TIP

You are not beholden to Java's whims. The variable that the imported class is stored in can be named whatever you like.

However, it's good practice to keep naming consistent between Lua and Java to avoid confusion. All Lua code on this wiki follows this standard.

Nested Class

In Lua, the last . between the outer and inner class becomes a $.

Lua
local OffsetType = require(
    "net.minecraft.world.level.block.state.BlockBehaviour$OffsetType"
)
Java
import net.minecraft.world.level.block.state.BlockBehaviour.OffsetType;

Object Construction

Lua
local ExampleObject = require("com.example.ExampleObject")

local example = ExampleObject(1, 2, 3) 
Java
import com.example.ExampleObject;
// ...
ExampleObject example = new ExampleObject(1, 2, 3); 

Static Methods & Fields

Lua
local ExampleObject = require("com.example.ExampleObject")

local value = ExampleObject.FIELD
ExampleObject.testMethod("hello!") 
Java
import com.example.ExampleObject;
// ...
int value = ExampleObject.FIELD; 
ExampleObject.testMethod("hello!"); 

Instance Methods & Fields

In Lua, the . changes to a : for instance method invocation.

Lua
local ExampleObject = require("com.example.ExampleObject")

local example = ExampleObject(1, 2, 3)
local value = example.field
local example:updateField(value+1) 
Java
import com.example.ExampleObject;
// ...
ExampleObject example = new ExampleObject(1, 2, 3);
double value = example.field; 
example.updateField(value+1); 

Casting

See Java Library - java.cast()

Lua
local ExampleObject = require("com.example.ExampleObject")
local CastedObject = require("com.example.CastedObject")

local example = ExampleObject(1, 2, 3)
local castedObject = java.cast(example, CastedObject) 
Java
import com.example.ExampleObject;
import com.example.CastedObject;
// ...
ExampleObject example = new ExampleObject(1, 2, 3);
CastedObject castedObject = (CastedObject) example; 

Type Conversions

Given a class like:

FooBar.java
java
package com.example;

// import ...

public class FooBar {
    private int integer;
    private long biginteger;
    private final List<Float> fineList = new ArrayList<>();
    private final List<Double> veryFineList = new ArrayList<>();

    public FooBar(int integer, long biginteger) {
        this.integer = integer;
        this.biginteger = biginteger;
    }

    public void addPrecise(float preciseNumber, double veryPreciseNumber) {
        fineList.add(preciseNumber);
        veryFineList.add(veryPreciseNumber);
    }

    public List<Float> getFineList() {
        return this.fineList;
    }

    public List<Double> getVeryFineList() {
        return this.veryFineList;
    }

    public Integer changeInteger(Integer newInteger) {
        Integer oldInteger = Integer.valueOf(this.integer);
        this.integer = newInteger;
        return oldInteger;
    }

    public Long changeBigInteger(Long newBigInteger) {
        Long oldBigInteger = Long.valueOf(this.biginteger);
        this.biginteger = newBigInteger;
        return oldBigInteger;
    }

    public boolean isLongString(String text) {
        return text.length() > 10;
    }

    public Boolean invert(Boolean value) {
        return Boolean.valueOf(!value);
    }
}

Primitive types (and their respective wrapper types) are seamlessly converted:

Lua
local FooBar = require("com.example.FooBar")

local foobar = FooBar(10, 7000000000)
foobar:addPrecise(0.1, 0.0001)
local fineList = foobar:getFineList()
local veryFineList = foobar:getVeryFineList()
local old = foobar:changeInteger(fineList:size())
local bigold = foobar:changeBigInteger(fineList:size()+veryFineList:size())
local longString = foobar:isLongString("Hello World!")
local notLongString = foobar:invert(longString)
Java
import com.example.FooBar;
// ...
FooBar foobar = new FooBar(10, 7000000000);
foobar.addPrecise(0.1f, 0.0001d);
List<Float> fineList = foobar.getFineList();
List<Double> veryFineList = foobar.getVeryFineList();
int old = foobar.changeInteger(fineList.size());
long bigold = foobar.changeBigInteger(fineList.size()+veryFineList.size());
boolean longString = foobar.isLongString("Hello World!");
boolean notLongString = foobar.invert(longString);

Types are converted as follows:

Lua TypeJava TypeNotes
nilnull, void, VoidCan only be used as void or Void when describing a method to create or override during class building.
numberbyte, short, int, long, float, double, Byte, Short, Int, Long, Float, DoubleIf a number greater than the max size of the Java type is provided, it overflows, wrapping around. (ex. if passing 256 where a byte is expected, the resulting byte will be 0)
booleanboolean, Boolean
stringString
functioninstanceWhen going from Lua to Java, converted to an instance of a functional interface (java interface with only one method), where applicable. see Callback Methods
tableList, Set, MapWhen going from Java to Lua, converted only if the java method's return value is annotated with @CoerceToNative. When going from Lua to Java, converted unconditionally.
userdatainstance, Class, EClassJava classes and objects are wrapped as userdata. Additionally, where a Class or EClass are expected, a userdata passed directly from require can be used.

INFO

In these docs, a userdata that wraps a class is represented as userdata [class], while a userdata that wraps an object is represented as userdata [instance]. On a userdata [class] static methods and fields are accessible, while on a userdata [instance] instance methods and fields are accessible.

Throwing exceptions

See Java Library - java.throw()

Lua
local IllegalStateException = require("java.lang.IllegalStateException")

java.throw(IllegalStateException("Oh no!")) 
Java
import java.lang.IllegalStateException;
// ...
throw new IllegalStateException("Oh no!"); 

Functional Interfaces

Lua
local ExampleObject = require("com.example.ExampleObject")

local example = ExampleObject(1, 2, 3)
local message = "Hello World!"
local numbers = example:getList()
numbers:forEach(function(otherObj) 
    otherObj:sendMessage(message) 
    otherObj:flush() 
end) 
Java
import com.example.ExampleObject;
// ...
ExampleObject example = new ExampleObject(1, 2, 3);
String message = "Hello World!";
List<SomeOtherObject> numbers = example.getList();
numbers.forEach((otherObj) -> { 
    otherObj.sendMessage(message); 
    otherObj.flush(); 
}); 

TIP

If the type is in the package java.util.function you can be confident it's a functional interface of some kind.

Generic Types

A generic class can be indexed with a table of classes before construction. This applies a lower boundary of possible types that the generic class can contain.

Lua
local ArrayList = require("java.util.ArrayList")
local Integer = require("java.lang.Integer")

local intList = ArrayList[{Integer}]() 
intList:addAll(3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 3, 9)
Java
import java.util.List;
import java.util.ArrayList;
import java.lang.Integer;
// ...
List<Integer> intList = new ArrayList<>(); 
intList.addAll(3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9, 3, 9);
// ...