Kotlin enum类与companion设计的不协调--NPE导致ClassNotFoundException, Could not initialize class.

    xiaoxiao2021-03-25  74

    环境: JDK8+kotlin 1.1.0 构建工具:Maven 3

    项目中有一个定义报警类型的enum类最开始使用java编写, 后来改成了kotlin. IDE和编译都没有提示错误, 运行时却发现错误.ClassNotFoundException, Could not initialize class.

    原来是kotlin使用companion代替static, 而enum类是在类初始化时就初始化实例, 如果在enum实例中引入到了companion object中的字段, ide和编译器都不会提示错误, 但是运行那么就会导致enum类初始化失败. 因为初始化实例时, companion object还没有初始化. 引用其字段, 导致NPE, 类初始化失败, 导致ClassNotFoundException, Could not initialize class.

    Java代码

    public enum AlarmType { MEMORY("memory", AlarmType.CONTAINER_TYPE, true), CPU("cpu", AlarmType.CONTAINER_TYPE, true); public static final int DEFAULT_TYPE = 0; public static final int CONTAINER_TYPE = 1; public static final int DOCKER_DAEMON_TYPE = 2; public static final int AGENT_TYPE = 3; private String type; private int generalType; AlarmType(String type, int generalType) { this.type = type; this.generalType = generalType; } }

    kotlin代码

    kotlin中没有static的概念, 由companion object来代替. 我将以上的代码修改为kotlin时为以下的代码.

    enum class AlarmType(val type: String, val generalType: Int) { MEMORY("memory", AlarmType.CONTAINER_TYPE), CPU("cpu", AlarmType.CONTAINER_TYPE); companion object { public val DEFAULT_TYPE = 0 public val CONTAINER_TYPE = 1 public val DOCKER_DAEMON_TYPE = 2 public val AGENT_TYPE = 3 } }

    kotlin字节码反编译

    IDE没有报错, 编译也没有报错. 运行时却发现不能运行. 报ClassNotFoundException异常, Could not initialize class. 我将kotlin生成的class字节码用jd-gui反编译看到以下代码.(适度精简) 由1和2处的代码, 可知, 在enum类中, 实例的初始化是在类加载完后, static代码块中运行, 因为在kotlin中使用companion object代替static, 代码从Companion中取值来实例化enum的实例时, 却发现Companion对象为空, 空指针错误, 进而导致无法初始化类, 导致ClassNotFoundException.

    public enum AlarmType { private final String type; public static final Companion Companion; private static final int DEFAULT_TYPE = 0; private static final int CONTAINER_TYPE = 1; private static final int DOCKER_DAEMON_TYPE = 2; private static final int AGENT_TYPE = 3; static { //enum类是在静态代码块中就初始化实例. 因为kotlin中, 使用companion替代static, 所以, 下面用了Companion对象. 在类初始化时, 导致NPE, 导致类不能初始化 AlarmType[] tmp6_2 = new AlarmType[2]; //1 tmp6_2[0] = (MEMORY = new AlarmType("MEMORY", 0, "memory", Companion.getCONTAINER_TYPE())); AlarmType[] tmp32_6 = tmp6_2; tmp32_6[1] = (CPU = new AlarmType("CPU", 1, "cpu", Companion.getCONTAINER_TYPE())); $VALUES = tmp32_6; //在初始化实例之后, 才初始化Companion对象. 导致出问题 Companion = new Companion(null); //2 } private final int generalType; protected AlarmType(String type, int generalType) { this.type = type; this.generalType = generalType; } public static final class Companion { public final int getDEFAULT_TYPE() { return AlarmType.access$getDEFAULT_TYPE$cp(); } public final int getCONTAINER_TYPE() { return AlarmType.access$getCONTAINER_TYPE$cp(); } public final int getDOCKER_DAEMON_TYPE() { return AlarmType.access$getDOCKER_DAEMON_TYPE$cp(); } public final int getAGENT_TYPE() { return AlarmType.access$getAGENT_TYPE$cp(); } } }

    解决方案

    改回java, 或者将companion object移动到另外一个类中. 这样enum初始化类执行static代码时就不会引用未初始化的Companion了.

    总结

    新语言还是有坑, 谷歌上都没有搜到这个错误. 如果是不应该这样用, 那么IDE和编译器就应该提示错误, 而不是等到运行时才报错.

    转载请注明原文地址: https://ju.6miu.com/read-40587.html

    最新回复(0)