Interface Injection
Interface injection is a type of class tweaking used to add interface implementations on Minecraft classes in the decompiled source.
The implementation being visible in the class's decompiled source removes the need to cast to the interface to use its methods.
Additionally, interface injections can be transitive, allowing libraries to more easily expose their added methods to mods that depend on them.
To showcase interface injection, this page's snippets will use an example where we add a new helper method to FlowingFluid.
Creating the Interface
In a package that is not your mixin package, create the interface you'd like to inject:
java
public interface BucketEmptySoundGetter {
default Optional<SoundEvent> example_mod$getBucketEmptySound() {
throw new AssertionError("Implemented in Mixin");
}
}1
2
3
4
5
2
3
4
5
In our case, we'll throw by default since we plan to implement the method through a mixin.
WARNING
The methods of injected interfaces must all be default to be injected with a class tweaker, even if you plan to implement the methods in the target class using a mixin.
Methods should also be prefixed by your mod ID with a separator such as $ or _ so that they cannot clash with other mods' added methods.
Implementing the Interface
TIP
If the interface's methods are fully implemented with the interface's defaults, you do not need to use a mixin to inject the interface, the class tweaker entry will be enough.
To create overrides of the interface's methods in the target class, you should use a mixin that implements the interface and targets the class you want to inject the interface into.
java
@Mixin(FlowingFluid.class)
abstract class FlowingFluidMixin extends Fluid implements BucketEmptySoundGetter {
@Override
public Optional<SoundEvent> example_mod$getBucketEmptySound() {
return Optional.of(this.is(FluidTags.LAVA) ? SoundEvents.BUCKET_EMPTY_LAVA : SoundEvents.BUCKET_EMPTY);
}
}1
2
3
4
5
6
7
2
3
4
5
6
7
The overrides will be added to the target class at runtime, but will not appear in the decompiled source even if you use class tweaker to make the interface implementation visible.
Making the Class Tweaker Entry
Interface injection uses the following syntax:
classtweaker
inject-interface <targetClassName> <injectedInterfaceName>For class tweaking, classes and interfaces use their internal names.
For our example interface, the entry would be:
classtweaker
inject-interface net/minecraft/world/level/material/FlowingFluid com/example/docs/interface_injection/BucketEmptySoundGetter1
Generic Interfaces
If your interface has generics, you can specify them in the entry. For this, add <> angled brackets at the end of the interface name with the generics in Java bytecode signature format between the brackets.
The signature format is:
| Description | Java example | Syntax | Signature Format Example |
|---|---|---|---|
| Class type | java.lang.String | Descriptor format | Ljava/lang/String; |
| Array type | java.lang.String[] | Descriptor format | [Ljava/lang/String; |
| Primitive | boolean | Descriptor character | Z |
| Type variable | T | T + name + ; | TT; |
| Generic class type | java.util.List<T> | L + internal name + < + generics + >; | Ljava/util/List<TT;>; |
| Wildcard | ?, java.util.List<?> | * character | *, java/util/List<*>; |
| Extends wildcard bound | ? extends String | + + the bound | +Ljava/lang/String; |
| Super wildcard bound | ? super String | - + the bound | -Ljava/lang/String; |
So to inject the interface:
java
package com.example.docs.interface_injection;
public interface GenericInterface<T, U> {
}1
2
3
4
5
2
3
4
5
with the generics <? extends String, Boolean[]>
The class tweaker entry would be:
classtweaker
inject-interface net/minecraft/world/level/material/FlowingFluid com/example/docs/interface_injection/GenericInterface<+Ljava/lang/String;[Ljava/lang/Boolean;>1
Applying Changes
To see your interface implementation applied, you must refresh your Gradle project by regenerating sources. If modifications do not appear, you can try validating the file and checking if any errors appear.
The added methods can now be used on instances of the class the interface was injected into:
java
void example(FlowingFluid flowingFluid) {
Optional<SoundEvent> sound = flowingFluid.example_mod$getBucketEmptySound();
/* ... */
}1
2
3
4
2
3
4
You can also override the methods in subclasses of the interface injection target if needed.








