You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否通过Interface扩展Enum以支持新增选项?Minecraft Forge API开发疑问

实现可扩展的Minecraft Forge数据类型系统

核心问题说明

Java枚举类本身是不可继承的,所以无法直接让其他模组给你的枚举添加新选项。要实现可扩展,得用「接口抽象+注册中心」的方案替代传统枚举的单一实现。

具体实现步骤

1. 定义数据类型接口

先把原有枚举的行为抽象成接口,让所有数据类型(包括你的原生类型和其他模组的自定义类型)都实现这个接口:

import java.util.function.Function;

public interface DataType<T> {
    // 返回唯一标识,用于区分不同类型
    String getId();
    // 将字符串解析为对应类型的对象
    T parse(String value);
    // 将对象序列化为字符串
    String serialize(T value);
}

2. 改造原有枚举实现接口

把你原来的枚举改成实现上面的DataType接口,保留原有逻辑:

import java.util.Date;
import java.time.LocalDate;
import java.util.function.Function;

public enum VanillaDataType implements DataType<Object> {
    BOOLEAN("boolean", Boolean::parseBoolean, Object::toString),
    DATE("date", s -> new Date(Long.parseLong(s)), d -> String.valueOf(((Date)d).getTime())),
    DOUBLE("double", Double::parseDouble, Object::toString),
    FLOAT("float", Float::parseFloat, Object::toString),
    INTEGER("integer", Integer::parseInt, Object::toString),
    LOCAL_DATE("local_date", LocalDate::parse, Object::toString);

    private final String id;
    private final Function<String, Object> parser;
    private final Function<Object, String> serializer;

    VanillaDataType(String id, Function<String, Object> parser, Function<Object, String> serializer) {
        this.id = id;
        this.parser = parser;
        this.serializer = serializer;
    }

    @Override
    public String getId() {
        return id;
    }

    @Override
    public Object parse(String value) {
        return parser.apply(value);
    }

    @Override
    public String serialize(Object value) {
        return serializer.apply(value);
    }
}

3. 实现数据类型注册中心

创建一个全局注册中心,用来管理所有注册的DataType实例,包括你的原生枚举和其他模组的自定义类型:

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class DataTypeRegistry {
    private static final Map<String, DataType<?>> REGISTRY = new HashMap<>();

    // 初始化时注册所有原生枚举类型
    static {
        for (VanillaDataType type : VanillaDataType.values()) {
            register(type);
        }
    }

    // 对外暴露的注册方法,其他模组调用这个方法添加自定义类型
    public static void register(DataType<?> dataType) {
        if (REGISTRY.containsKey(dataType.getId())) {
            throw new IllegalArgumentException("数据类型ID " + dataType.getId() + " 已存在!");
        }
        REGISTRY.put(dataType.getId(), dataType);
    }

    // 根据ID获取对应的数据类型
    public static DataType<?> getById(String id) {
        return REGISTRY.get(id);
    }

    // 获取所有已注册的数据类型
    public static Collection<DataType<?>> getAllTypes() {
        return Collections.unmodifiableCollection(REGISTRY.values());
    }
}

4. 其他模组扩展自定义类型

其他模组开发者只需要实现DataType接口,然后在模组初始化阶段调用注册方法即可:

import java.math.BigDecimal;

public class BigDecimalDataType implements DataType<BigDecimal> {
    @Override
    public String getId() {
        return "big_decimal";
    }

    @Override
    public BigDecimal parse(String value) {
        return new BigDecimal(value);
    }

    @Override
    public String serialize(BigDecimal value) {
        return value.toPlainString();
    }
}

// 在模组的初始化事件中注册(比如FMLCommonSetupEvent)
// @SubscribeEvent
// public void onCommonSetup(FMLCommonSetupEvent event) {
//     DataTypeRegistry.register(new BigDecimalDataType());
// }

关于接口扩展枚举的问题

Java中无法通过接口直接扩展枚举的选项,因为枚举类默认继承java.lang.Enum,而Java不支持多继承,枚举类只能实现接口,但不能被其他类继承。不过上面的方案正是利用接口的抽象能力,把枚举的行为和实现解耦,让自定义类型和原生枚举站在同一层级,间接实现了「扩展枚举选项」的效果。

Forge开发注意事项

  • 注册时机:其他模组应该在FMLCommonSetupEvent或更晚的初始化事件中注册自定义类型,避免在模组加载早期注册导致的问题。
  • 线程安全:Forge的模组初始化是单线程执行的,所以注册中心无需额外加锁,但如果有动态注册需求,需要自行保证线程安全。

内容的提问来源于stack exchange,提问作者CuteKitsuneForever

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 05:03:33