接口注入
接口注入是一种类调整,用于在反编译源码中为 Minecraft 类添加接口实现。
由于该实现会显示在类的反编译源码中,因此在使用接口中的方法时,无需再强制转换为该接口类型。
除此之外,接口注入还可以是传递性的,这使得库能够更方便地向依赖它们的模组暴露新增的方法。
为了演示接口注入,本页的代码片段将使用一个示例:为 FlowingFluid 添加一个新的辅助方法。
创建接口
在一个非 mixin 包中,创建你想要注入的接口:
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
在本例中,我们会默认抛出异常,因为我们计划通过 mixin 来实现该方法。
WARNING
通过类调整器注入的接口,其所有方法都必须是 default 方法,即使你计划使用 mixin 在目标类中实现这些方法也是如此。
方法名也应当以你的模组 ID 作为前缀,并使用 $ 或 _ 等分隔符,这样可以避免与其他模组添加的方法发生冲突。
实现接口
TIP
如果接口中的方法已经通过接口自身的 default 实现完整实现,那么你不需要使用 mixin 来注入接口,只需要添加类调整器条目即可。
若要在目标类中重写接口方法,你应当使用一个实现该接口、并以目标类为目标的 mixin。
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
这些重写方法会在运行时被添加到目标类中,但即使你使用类调整器让接口实现显示出来,它们也不会出现在反编译源码中。
制作类调整器条目
接口注入使用以下语法:
inject-interface <targetClassName> <injectedInterfaceName>在类调整中,类和接口都使用它们的内部名称。
对于我们的示例接口,条目如下:
classtweaker
inject-interface net/minecraft/world/level/material/FlowingFluid com/example/docs/interface_injection/BucketEmptySoundGetter泛型接口
如果你的接口带有泛型,也可以在条目中指定泛型。 为此,需要在接口名称末尾添加 <> 尖括号,并在括号中使用 Java 字节码签名格式填写泛型。
签名格式如下:
| 描述 | Java 示例 | 语法 | 签名格式示例 |
|---|---|---|---|
| 类类型 | java.lang.String | 类型描述符格式 | Ljava/lang/String; |
| 数组类型 | java.lang.String[] | 类型描述符格式 | [Ljava/lang/String; |
| 原始类型 | boolean | 类型描述符字符 | Z |
| 类型参数 | T | T + 名称 + ; | TT; |
| 泛型类类型 | java.util.List<T> | L + 内部名称 + < + 泛型 + >; | Ljava/util/List<TT;>; |
| 通配符 | ?、java.util.List<?> | * 字符 | *, java/util/List<*>; |
| 上界通配符 | ? extends String | + + 边界类型 | +Ljava/lang/String; |
| 下界通配符 | ? super String | - + 边界类型 | -Ljava/lang/String; |
因此,如果要注入以下接口:
java
package com.example.docs.interface_injection;
public interface GenericInterface<T, U> {
}1
2
3
4
5
2
3
4
5
并指定泛型 <? extends String, Boolean[]>,
那么类调整器条目应为:
classtweaker
inject-interface net/minecraft/world/level/material/FlowingFluid com/example/docs/interface_injection/GenericInterface<+Ljava/lang/String;[Ljava/lang/Boolean;>应用更改
若要看到接口实现生效,你必须刷新 Gradle 项目并重新生成源码。 如果修改没有出现,可以尝试验证该文件,并检查是否有错误。
现在,新增的方法可以直接在被注入接口的类实例上使用:
java
void example(FlowingFluid flowingFluid) {
Optional<SoundEvent> sound = flowingFluid.example_mod$getBucketEmptySound();
/* ... */
}1
2
3
4
2
3
4
如有需要,你也可以在接口注入目标类的子类中重写这些方法。









