User Tools

Site Tools


tutorial:interface_injection

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
tutorial:interface_injection [2025/11/10 02:07] – Add headings to interface injection article earthcomputertutorial:interface_injection [2025/12/27 12:27] (current) gauntrecluse
Line 6: Line 6:
 More specifically, you can create an interface, and then inject this interface into the class. More specifically, you can create an interface, and then inject this interface into the class.
 As result the target class will acquire all the methods of the interface, as if it always had them. As result the target class will acquire all the methods of the interface, as if it always had them.
-Interface injection is a compile time only feature, this means that a Mixin should also be used to implement the interface into the target class. 
  
 This is particularly useful for libraries, with this you can add new methods to existing classes and use them without the need of casting or reimplementing the interface every time. This is particularly useful for libraries, with this you can add new methods to existing classes and use them without the need of casting or reimplementing the interface every time.
Line 17: Line 16:
  
 <yarncode java [enable_line_numbers="false"]> <yarncode java [enable_line_numbers="false"]>
-Optional<class_3414> getBucketEmptySound$myMod()+Optional<class_3414> modid$getBucketEmptySound()
 </yarncode> </yarncode>
  
Line 28: Line 27:
  
 public interface BucketEmptySoundGetter { public interface BucketEmptySoundGetter {
- default Optional<class_3414> getBucketEmptySound$myMod() {+ default Optional<class_3414> modid$getBucketEmptySound() {
  return Optional.empty();  return Optional.empty();
  }  }
Line 34: Line 33:
 </yarncode> </yarncode>
  
-:!: The method body in the interface may not be used because it will be overridden by the mixin class. However, you must specify the method body, which means the method must be ''default''. You can make it return null or throw ''UnsupportedOperationException'', but you //cannot// make it abstract, such as ''Optional<yarn class_3414> getBucketEmptySound()'', or exceptions will be thrown when compiling!+:!: Even if the method body in the interface isn't supposed to be used because it will be overridden by the mixin class, you must still generally specify the method body, which means the method must be ''default''. You can make it return null or throw ''UnsupportedOperationException'', but you //cannot// make it abstract, such as ''Optional<yarn class_3414> modid$getBucketEmptySound()'', or exceptions will be thrown when compiling!
  
-ℹ️ It's highly recommended to add a dollar-character or underscore character with the mod name as the prefix or suffix of the method name, in order to avoid method name conflict with other mods.+ℹ️ If implementing the method with an interface, it's highly recommended to add a dollar-character or underscore character with the mod name as the prefix or suffix of the method name, in order to avoid method name conflict with other mods.
  
 ===== Step 2: Implement the Interface with a Mixin ===== ===== Step 2: Implement the Interface with a Mixin =====
  
-Now you need to implement this interface into ''<yarn net.minecraft.class_3609>'' with a mixin implementing the interface:+The class tweaker step below on its own will be enough if you only want to implement the interface (for example, for marker interfaces, or where you have the whole implementation in default methods). However, you need to implement this interface on a mixin to ''<yarn net.minecraft.class_3609>'' if you want to implement any methods without providing the implementation in a default method:
  
 <yarncode java [enable_line_numbers="false"]> <yarncode java [enable_line_numbers="false"]>
Line 46: Line 45:
 abstract class MixinFlowableFluid implements BucketEmptySoundGetter { abstract class MixinFlowableFluid implements BucketEmptySoundGetter {
  @Override  @Override
- public Optional<class_3414> getBucketEmptySound$myMod() {+ public Optional<class_3414> modid$getBucketEmptySound() {
      //This is how to get the default sound, copied from BucketItem class.      //This is how to get the default sound, copied from BucketItem class.
      return Optional.of(((class_3609) (Object) this).method_15791(class_3486.field_15518) ? class_3417.field_15010 : class_3417.field_14834);      return Optional.of(((class_3609) (Object) this).method_15791(class_3486.field_15518) ? class_3417.field_15010 : class_3417.field_14834);
Line 53: Line 52:
 </yarncode> </yarncode>
  
-===== Step 3: Inject the Interface in ''fabric.mod.json'' =====+ 
 +===== Step 3: Inject the Interface in the class tweaker =====
  
 Lastly you need to inject the interface into ''<yarn net.minecraft.class_3609>''. Lastly you need to inject the interface into ''<yarn net.minecraft.class_3609>''.
-The following snippet can be added to your ''fabric.mod.json'' file to add one or more interfaces to the ''<yarn net.minecraft.class_3609>'' class.+The following snippet can be added to your class tweaker file to add one or more interfaces to the ''<yarn net.minecraft.class_3609>'' class
 +Follow the [[tutorial:accesswidening|access widening tutorial]] for how to create a class tweaker.
 Note that all class names here must use the "internal names" that use slashes instead of dots (''path/to/my/Class''). Note that all class names here must use the "internal names" that use slashes instead of dots (''path/to/my/Class'').
  
-<code json fabric.mod.json [enable_line_numbers="false"]> +<code - mymod.classtweaker [enable_line_numbers="false"]> 
-{ +classTweaker v1 named 
- "custom": { + 
- "loom:injected_interfaces":+inject-interface net/minecraft/fluid/FlowableFluid net/fabricmc/example/BucketEmptySoundGetter
- "net/minecraft/class_3609": ["net/fabricmc/example/BucketEmptySoundGetter"] +
-+
-+
-}+
 </code> </code>
  
-:!: Sometimes, your interface injections may need to include the ''$'' symbolbut the Groovy template processor may interpret this as a template variable if you are replacing variables (such as ''${version}''in your ''fabric.mod.json''. A workaround for this is to use the Unicode escape for ''$'', which is ''\u0024''.+:!: To make your interface injection visible to other mods depending on your mod (if you are writing a library mod), use ''transitive-inject-interface'' instead of ''inject-interface''
 + 
 +==== Generic interfaces ==== 
 + 
 +If your interface has genericsyou can specify them when you add the injected interface. For thisyou need to add ''<>'' angled brackets and write the generics in Java bytecode signature format between them. 
 + 
 +^ Description                                   ^ Java example                 ^ Syntax                                                                                                                                                                         ^ Signature format example       ^ 
 +| Class type                                    | ''java.lang.String''         | ''L'' + internal name + '';''                                                                                                                                                  | ''Ljava/lang/String;''         | 
 +| Array type                                    | ''java.lang.String[]''       | ''['' + element type                                                                                                                                                           | ''[Ljava/lang/String;''        | 
 +| Primitive type (may appear as array elements) | ''double''                   single capital letter representing the type. Mostly logical, such as ''int''''I'', ''double'' = ''D'', with the 2 exceptions of ''boolean'' = ''Z'' and ''long'' = ''J''| ''D''                          | 
 +| 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<?>'' | The ''*'' character                                                                                                                                                            | ''*'', ''Ljava/util/List<*>;''
 +| Extends wildcard bound                        | ''? extends String''         | ''+'' + the bound                                                                                                                                                              | ''+Ljava/lang/String;''        | 
 +| Super wildcard bound                          | ''? super String''           | ''-'' + the bound                                                                                                                                                              | ''-Ljava/lang/String;''        | 
 + 
 +Here is a full example using generics: 
 +<code - mymod.classtweaker [enable_line_numbers="false"]> 
 +classTweaker v1 named 
 + 
 +inject-interface net/minecraft/fluid/FlowableFluid net/fabricmc/example/MyGenericInterface<+TT;TU;> 
 +</code> 
 +which would generate the implementation: 
 +<yarncode java [enable_line_numbers="false"]> 
 +public class class_3609 implements MyGenericInterface<? extends T, U> { 
 +   // ... 
 +
 +</yarncode>
  
 ===== Step 4: Using the Injected Method ===== ===== Step 4: Using the Injected Method =====
Line 76: Line 100:
  
 <yarncode java [enable_line_numbers="false"]> <yarncode java [enable_line_numbers="false"]>
-Optional<class_3414> sound = mytestfluid.getBucketEmptySound$myMod();+Optional<class_3414> sound = mytestfluid.modid$getBucketEmptySound();
 </yarncode> </yarncode>
  
tutorial/interface_injection.1762740421.txt.gz · Last modified: 2025/11/10 02:07 by earthcomputer