registrateAPI——非空函数
Registrate简介
Registrate 是一个面向 Minecraft 模组开发者 的 库模组。它的核心目标是简化并优化模组开发过程中的内容注册流程。
1. 核心概念:什么是“注册”?
在Minecraft模组开发中,“注册”是一个至关重要的步骤。开发者创建的任何新内容,如:
- 物品:新的工具、武器、材料。
- 方块:新的建筑方块、功能性方块。
- 实体:新的生物、投射物。
- 状态效果:新的药水效果。
- 声音、粒子效果等等。
这些内容都需要在游戏启动时向游戏的核心系统(“注册表”)进行登记,游戏才能识别并使用它们。这个过程传统上需要编写大量重复、繁琐且容易出错的代码。
Registrate 就是为了解决这个问题而生的。
2. 模组介绍
- 性质:它是一个库模组。这意味着它的主要用户是其他模组的开发者,而不是普通玩家。
- 内置性:正如短评和简介中提到的,“大多数使用它的模组内置它,所以玩家无需下载”。你几乎不会需要单独安装它,因为它会作为依赖库被整合在你喜欢的功能性模组中。
- 支持广泛:它同时支持 Forge 和 Fabric 两大模组加载器,并且支持从 1.14.4 到最新版 1.21.1 的众多Minecraft版本,这显示了其强大的兼容性和在开发者社区中的流行度。
- 开发团队:由
tterrag
主导开发,并有TropheusJ
,AlphaMode
,Fabricators-of-Create
等开发者或团队为其Fabric版本做出贡献。
3. 主要作用与优势
Registrate 通过提供一套流畅的API(应用程序接口),为开发者带来了诸多好处:
-
极大简化注册代码
- 传统方式:注册一个方块可能需要多行分散的代码,分别处理创建、注册、设置属性等。
- Registrate方式:采用链式调用,可以将创建、注册、设置名称、设置属性、设置材质模型、设置标签等一系列操作,用一条清晰、连贯的语句完成。这使得代码非常简洁、易读且易于维护。
-
自动化模型和语言文件生成
- 这是一个非常强大的功能。开发者只需在代码中指定基础材质路径,Registrate就能自动生成该物品/方块的
.json
模型文件、方块状态文件以及语言文件(如en_us.json
中的本地化名称)。 - 这省去了开发者手动编写大量重复的JSON文件的工作,极大地提高了开发效率,并减少了因手误导致的错误。
- 这是一个非常强大的功能。开发者只需在代码中指定基础材质路径,Registrate就能自动生成该物品/方块的
-
出色的组织性和可读性
- 由于所有相关操作都在一个“流水线”上完成,代码逻辑非常清晰。其他开发者阅读代码时,可以很容易地追踪一个物品从创建到注册完毕的完整生命周期。
-
集成最佳实践
- Registrate的API设计引导开发者使用当前模组加载器推荐的注册方式,有助于写出更规范、更安全的代码。
4. 总结与类比
你可以把 Registrate 理解为模组开发领域的 “高级助手” 或 “代码脚手架工具”。
- 对开发者而言:它就像是一个强大的IDE(集成开发环境)插件,能自动完成那些繁琐、重复的编码任务,让开发者可以更专注于模组核心功能和创意的实现。
- 对玩家而言:你虽然看不见它,但它帮助了你喜欢的模组(例如短评中提到的机械动力就内置了它)更快、更稳定地被开发出来。你享受到的各种新物品、新方块,其背后可能就有Registrate的功劳。
总而言之,Registrate是一个旨在提升Minecraft模组开发效率、减少冗余代码、自动化繁琐任务的开发者工具库,是许多现代模组项目不可或缺的基石之一。
NonNullSupplier
类概述
NonNullSupplier
是一个函数式接口,继承自Supplier<T>
,专门用于提供非空值的供应商。
@FunctionalInterface
public interface NonNullSupplier<@NonnullType T> extends Supplier<T>
与 Supplier 的区别和联系
联系
- 继承关系:
NonNullSupplier
继承自Supplier<T>
- 核心方法:都包含
T get()
方法 - 函数式接口:都可以用lambda表达式或方法引用实现
区别
方面 | Supplier | NonNullSupplier |
---|---|---|
空值约束 | 可能返回null | 保证不返回null |
注解 | 无特殊注解 | 使用@NonnullType 注解 |
错误处理 | 无内置空值检查 | 内置null检查并抛出异常 |
设计目的 | 通用供应商 | 专门用于非空场景 |
方法详解
1. 核心get方法
@Override
T get(); // 保证返回非空值
2. 静态工厂方法 - 基础版本
static <T> NonNullSupplier<T> of(Supplier<@NullableType T> sup)
作用:将可能返回null的Supplier转换为NonNullSupplier
示例:
// 可能返回null的普通Supplier
Supplier<String> nullableSupplier = () -> Math.random() > 0.5 ? "hello" : null;// 转换为NonNullSupplier - 遇到null会抛出异常
NonNullSupplier<String> nonNullSupplier = NonNullSupplier.of(nullableSupplier);// 使用时有非空保证
String value = nonNullSupplier.get(); // 不会返回null,遇到null会抛出异常
3. 静态工厂方法 - 自定义错误信息
static <T> NonNullSupplier<T> of(Supplier<@NullableType T> sup, NonNullSupplier<String> errorMsg)
示例:
Supplier<String> configSupplier = () -> loadConfigFromFile();// 使用自定义错误信息
NonNullSupplier<String> safeConfig = NonNullSupplier.of(configSupplier, () -> "配置文件加载失败,请检查配置路径"
);// 当configSupplier返回null时,会抛出包含自定义信息的NullPointerException
4. 懒加载方法
default NonNullSupplier<T> lazy() { return lazy(this); }static <T> NonNullSupplier<T> lazy(Supplier<@NonnullType T> sup) {return Lazy.of(sup)::get;
}
作用:创建延迟初始化的NonNullSupplier,值只在第一次访问时计算并缓存。
示例:
// 创建昂贵的资源供应商
NonNullSupplier<ExpensiveResource> resourceSupplier = () -> createExpensiveResource();// 转换为懒加载版本
NonNullSupplier<ExpensiveResource> lazyResource = resourceSupplier.lazy();// 或者使用静态方法
NonNullSupplier<ExpensiveResource> lazyResource2 = NonNullSupplier.lazy(resourceSupplier);// 第一次调用get()会执行createExpensiveResource()并缓存结果
// 后续调用直接返回缓存值
ExpensiveResource resource = lazyResource.get();
完整使用示例
public class NonNullSupplierExample {// 可能返回null的方法private static String loadFromDatabase(int id) {return id == 1 ? "UserData" : null;}// 创建NonNullSupplier包装器private static NonNullSupplier<String> createSafeDataLoader(int id) {Supplier<String> rawSupplier = () -> loadFromDatabase(id);return NonNullSupplier.of(rawSupplier, () -> "数据库查询失败,ID: " + id);}public static void main(String[] args) {// 用例1: 基础使用NonNullSupplier<String> dataLoader = createSafeDataLoader(1);String data = dataLoader.get(); // 正常返回 "UserData"// 用例2: 遇到null的情况NonNullSupplier<String> invalidLoader = createSafeDataLoader(2);try {String invalidData = invalidLoader.get(); // 抛出NullPointerException} catch (NullPointerException e) {System.out.println(e.getMessage()); // 输出: 数据库查询失败,ID: 2}// 用例3: 懒加载NonNullSupplier<String> lazyLoader = createSafeDataLoader(1).lazy();// 实际数据库查询只在第一次调用get()时发生String lazyData = lazyLoader.get();}
}
设计模式与优势
1. 防御性编程
// 传统方式需要手动检查
String value = supplier.get();
if (value == null) {throw new IllegalStateException("值不能为null");
}// 使用NonNullSupplier - 内置检查
String value = nonNullSupplier.get(); // 自动进行null检查
2. 装饰器模式
NonNullSupplier
包装了原有的Supplier,增加了非空保证的功能。
3. 延迟初始化模式
通过lazy()
方法实现了值的延迟计算和缓存。
使用场景
- 配置加载:保证配置值不为null
- 资源管理:确保资源正确初始化
- 依赖注入:保证依赖对象不为空
- 缓存系统:懒加载昂贵的计算或IO操作
总结
NonNullSupplier
是对标准Supplier
的增强,提供了:
- 强制的非空保证
- 内置的空值检查机制
- 灵活的懒加载支持
- 更好的代码可读性和安全性
它特别适合在需要保证返回值不为null,且希望将空值检查逻辑封装起来的场景中使用。
NonNullFunction
类概述
@FunctionalInterface
public interface NonNullFunction<@NonnullType T, @NonnullType R> extends Function<T, R>
这是一个函数式接口,继承自Function<T, R>
,专门设计用于输入和输出都保证非空的函数转换操作。
核心设计目的
1. 类型安全保证
- 输入非空:参数
T
使用@NonnullType
注解 - 输出非空:返回值
R
使用@NonnullType
注解 - 编译期检查:通过注解在编译期提供额外的空安全保证
2. 与标准Function的区别
方面 | Function<T, R> | NonNullFunction<T, R> |
---|---|---|
输入约束 | 可能接受null | 保证不接受null输入 |
输出约束 | 可能返回null | 保证不返回null |
组合安全 | 组合时可能NPE | 组合时避免NPE风险 |
注解支持 | 无特殊注解 | 使用@NonnullType 注解 |
方法详解
1. 核心apply方法
@Override
R apply(T t); // 保证:t != null 且 返回值 != null
2. andThen组合方法
default <V> NonNullFunction<T, V> andThen(NonNullFunction<? super R, ? extends V> after) {Objects.requireNonNull(after);return t -> after.apply(apply(t));
}
关键改进:
- 参数要求必须是
NonNullFunction
,而不是普通的Function
- 内部使用
Objects.requireNonNull
检查组合函数不为null - 返回的仍然是
NonNullFunction
,保持非空特性链
完整使用示例
基础使用
public class NonNullFunctionExample {public static void main(String[] args) {// 创建NonNullFunction实例NonNullFunction<String, Integer> stringToLength = s -> s.length();NonNullFunction<Integer, String> lengthToDescription = len -> "长度为: " + len;// 单个函数应用Integer length = stringToLength.apply("hello"); // 返回 5String desc = lengthToDescription.apply(5); // 返回 "长度为: 5"// 函数组合NonNullFunction<String, String> composed = stringToLength.andThen(lengthToDescription);String result = composed.apply("world"); // 返回 "长度为: 5"}
}
实际应用场景
public class DataProcessor {// 数据处理管道 - 每个步骤都保证非空public NonNullFunction<String, String> createProcessingPipeline() {NonNullFunction<String, String> trim = String::trim;NonNullFunction<String, String> toUpperCase = String::toUpperCase;NonNullFunction<String, String> addPrefix = s -> "PROCESSED: " + s;// 安全组合,不会出现NPEreturn trim.andThen(toUpperCase).andThen(addPrefix);}// 使用示例public void processData() {NonNullFunction<String, String> pipeline = createProcessingPipeline();String input = " hello world "; // 非空输入String output = pipeline.apply(input); // 返回 "PROCESSED: HELLO WORLD"System.out.println(output);}
}
与普通Function的对比
public class ComparisonExample {// 普通Function - 可能返回nullFunction<String, Integer> unsafeFunction = s -> {if (s.isEmpty()) return null; // 可能返回null!return s.length();};// NonNullFunction - 保证不返回nullNonNullFunction<String, Integer> safeFunction = s -> {if (s.isEmpty()) return 0; // 必须返回非空值return s.length();};// 组合时的差异public void demonstrateDifference() {// 普通Function组合 - 可能NPEFunction<String, String> unsafeComposed = unsafeFunction.andThen(len -> "长度: " + len // 如果len为null,这里会NPE!);// NonNullFunction组合 - 安全NonNullFunction<String, String> safeComposed = safeFunction.andThen(len -> "长度: " + len // len保证不为null);}
}
高级用法模式
1. 构建处理管道
public class ProcessingPipeline {public static NonNullF