User Tools

Site Tools


zh_cn:tutorial:reflection

Table of Contents

反射

与常规的 Java 开发相比,由于 Minecraft 在官方(混淆)、中间名和映射的(yarn 或者 mojmap)名称之间重映射,因此,通过反射寻找特定的字段或者方法需要格外小心。如果类是由原始的字符串名称找到的,例如 Class.forName,这些类也需要格外小心。(常量引用,如 MinecraftClient.class,由重映射器处理。)

重映射

参见:mappings

反射查找方法或字段时,建议通过中间名引用,因为在不同的 Minecraft 版本之间,中间名往往是保持恒定的(借助匹配),反射的代码可以在新版本的 Minecraft 中运行而无需发布新的模组版本。

例子:

// 我们将使用解析器将中间名转换为运行时名称
MappingResolver resolver = FabricLoader.getInstance().getMappingResolver();
 
Class<?> cls;
// 将中间类名转化为运行时类名的例子
cls = Class.forName(resolver.mapClassName("intermediary", "net.minecraft.class_2960"));
// 你也可以只适用类的常量引用,重映射器会处理:
cls = Identifier.class;
 
// 为 Identifier.getNamespace 创建一个方法处理:
MethodHandles.Lookup lookup = MethodHandles.publicLookup(); // public 方法
MethodHandle namespaceGetter = lookup.findVirtual(cls,
  resolver.mapMethodName(
    "intermediary",
    // 反映射的例子,简单输出 "net.minecraft.class_2960"
    resolver.unmapClassName("intermediary", cls.getName()),
    "method_12836",
    "()Ljava/lang/String;"
  ),
  MethodType.methodType(String.class)
);

记录

自从 1.18,原版 Minecraft 在代码中开始使用 记录。但是,由于 Minecraft 是使用 Proguard 处理的,会从 Minecraft 类中移除记录信息,因此这些记录类会在运行时有如下的行为,尽管在代码中反编译成了记录:

recordClass.isRecord() == false
recordClass.getRecordComponents() == null

结果就是,您无法通过反射找到原版类的记录组件。

参见 isRecord()getRecordComponents() 的 JDK 17 API 文档。

而且,proguard 会移除从记录中移除表示通用信息的签名(但不会从方法中移除)。Yarn 映射为这些类定义签名映射。这会影响对一些反射方法的调用的反射结果,例如 recordClass.getTypeParameters(),在引用了没有类型参数的记录的方法中调用 getGenericReturnType() 可能会造成 MalformedParameterizedTypeException。

zh_cn/tutorial/reflection.txt · Last modified: 2022/05/04 12:19 by 127.0.0.1