zh_cn:tutorial:reflection
反射
与常规的 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