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

使用Kryo序列化iCal4j Calendar失败:缺少无参构造方法

问题:Kryo序列化iCal4j内部类ImmutableCalScale失败

项目中自定义了Calendar类,包含iCal4j的Calendar对象:

public class Calendar implements Serializable{

private static final long serialVersionUID = 1L;

private net.fortuna.ical4j.model.Calendar calendar;
private VTimeZone timezone;


/*
 * constructor
 */
public Calendar() {
    super();
    
    //Create a TimeZone
    TimeZoneRegistry registry = TimeZoneRegistryFactory.getInstance().createRegistry();
    this.timezone = registry.getTimeZone("Europe/Berlin").getVTimeZone();
    
    // Create a calendar
    this.calendar = createCalendar();
}

Kryo配置代码:

Kryo kryo = new Kryo();
kryo.setRegistrationRequired(false);
kryo.setReferences(true);


kryo.register(DateTime.class, new JodaDateTimeSerializer());
kryo.register(java.util.UUID.class, new UUIDSerializer());
kryo.register(net.fortuna.ical4j.model.component.VEvent.class, new Ical4jVEventSerializer());
kryo.register(net.fortuna.ical4j.model.property.CalScale.class, new JavaSerializer());

EstateManager earth = (EstateManager) o;

Output output = new Output(new FileOutputStream(filekyro));
kryo.writeClassAndObject(output, earth.getApplicants().get(0));
output.close();

执行序列化时抛出异常:

com.esotericsoftware.kryo.KryoException: Class cannot be created (missing no-arg 
constructor): net.fortuna.ical4j.model.property.CalScale$ImmutableCalScale
Serialization trace:
properties (net.fortuna.ical4j.model.Calendar)
calendar (core.Calendar)
calendar (core.EstateManager)
earth (core.Country)
country (core.City)
city (core.Applicant)
at com.esotericsoftware.kryo.util.DefaultInstantiatorStrategy.newInstantiatorOf(DefaultInstantiatorStrategy.java:111)
at com.esotericsoftware.kryo.Kryo.newInstantiator(Kryo.java:1190)
at com.esotericsoftware.kryo.Kryo.newInstance(Kryo.java:1199)
at com.esotericsoftware.kryo.serializers.FieldSerializer.create(FieldSerializer.java:163)
at com.esotericsoftware.kryo.serializers.FieldSerializer.read(FieldSerializer.java:122)

尝试注册ImmutableCalScale但提示无法解析类型,使用JavaSerializer也无效,求可行解决方案。


可行解决方案

1. 替换实例化策略绕过无参构造器要求

Kryo默认的DefaultInstantiatorStrategy要求类必须有无参构造器,而ImmutableCalScale是私有内部类且没有无参构造。可以替换为Objenesis的实例化策略,它能在不调用构造器的情况下创建对象:

// 使用StdInstantiatorStrategy(Kryo内置,无需额外依赖)
kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());

// 或者如果需要更强的兼容性,引入Objenesis库后使用:
// kryo.setInstantiatorStrategy(new ObjenesisInstantiatorStrategy());

若使用ObjenesisInstantiatorStrategy,需添加Maven依赖:

<dependency>
    <groupId>org.objenesis</groupId>
    <artifactId>objenesis</artifactId>
    <version>3.3</version>
</dependency>

2. 为CalScale编写自定义Kryo序列化器

CalScale本质是枚举风格的类,ImmutableCalScale是其常量实例。可以自定义序列化器,直接序列化实例的标识字符串,反序列化时通过iCal4j提供的工厂方法获取实例:

public class CalScaleSerializer extends Serializer<CalScale> {
    @Override
    public void write(Kryo kryo, Output output, CalScale calScale) {
        // 序列化CalScale的标识值,如"GREGORIAN"
        output.writeString(calScale.getValue());
    }

    @Override
    public CalScale read(Kryo kryo, Input input, Class<? extends CalScale> type) {
        // 通过标识值获取对应的CalScale单例实例
        return CalScale.getInstance(input.readString());
    }
}

注册该序列化器:

kryo.register(net.fortuna.ical4j.model.property.CalScale.class, new CalScaleSerializer());

这种方式无需关心内部类ImmutableCalScale,完全基于iCal4j的API实现,兼容性强。

3. 通过反射注册ImmutableCalScale类

若要直接处理该内部类,可通过反射获取其Class对象并注册序列化器:

try {
    Class<?> immutableCalScaleClass = Class.forName("net.fortuna.ical4j.model.property.CalScale$ImmutableCalScale");
    kryo.register(immutableCalScaleClass, new JavaSerializer());
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

注意:该方法依赖类的全限定名,若iCal4j后续版本修改内部类结构会失效,不如方案2可靠。

4. 借助iCal4j自身序列化机制

既然ical4j-serializer不兼容Kryo,可以先将iCal4j的Calendar对象序列化为字符串/字节数组,再用Kryo序列化该结果。为自定义Calendar类编写序列化器:

public class CustomCalendarSerializer extends Serializer<Calendar> {
    @Override
    public void write(Kryo kryo, Output output, Calendar customCal) {
        // 用iCal4j自带工具将Calendar序列化为字符串
        StringWriter writer = new StringWriter();
        try {
            new CalendarOutput(writer).write(customCal.getCalendar());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        output.writeString(writer.toString());
        // 序列化timezone字段
        kryo.writeObject(output, customCal.getTimezone());
    }

    @Override
    public Calendar read(Kryo kryo, Input input, Class<? extends Calendar> type) {
        Calendar customCal = new Calendar();
        // 反序列化iCal4j的Calendar对象
        String icalStr = input.readString();
        CalendarBuilder builder = new CalendarBuilder();
        try {
            customCal.setCalendar(builder.build(new StringReader(icalStr)));
        } catch (IOException | ParserException e) {
            throw new RuntimeException(e);
        }
        // 反序列化timezone字段
        customCal.setTimezone(kryo.readObject(input, VTimeZone.class));
        return customCal;
    }
}

注册该序列化器:

kryo.register(core.Calendar.class, new CustomCalendarSerializer());

这种方式完全利用iCal4j自身的序列化逻辑,避免处理内部类问题,兼容性最好,但序列化后的体积会比直接用Kryo略大。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 14:20:22