Kotlin如何不使用反射遍历Sealed Class密封类对象?
Kotlin密封类无反射遍历实现方案
问题背景
需要遍历如下定义的密封类NavigationItem的所有子类实例:
sealed class NavigationItem(val route: String, val icon: Int, val title: String, val color: String) { object Home : NavigationItem("home", R.drawable.ic_home, "Home", "#FFFF00") object Music : NavigationItem("music", R.drawable.ic_music, "Music", "#FF00FF") object Movies : NavigationItem("movies", R.drawable.ic_movie, "Movies", "#00FFFF") object Books : NavigationItem("books", R.drawable.ic_book, "Books", "#FFAAAA") object Profile : NavigationItem("profile", R.drawable.ic_profile, "Profile", "#AAAAFF") }
最初使用sealedSubclasses反射API实现遍历:
NavigationItem::class.sealedSubclasses.forEach { it.objectInstance?.apply { // 对遍历到的每个对象执行对应逻辑 } }
运行时抛出KotlinReflectionNotSupportedError,提示缺少kotlin-reflect.jar依赖,需求是在不使用反射的前提下实现遍历,确认是否必须改用枚举类。
实现方案
方案1:保留密封类,手动维护实例列表(推荐)
不需要强制改用枚举,也不需要引入反射依赖,直接在密封类的伴生对象中定义所有子类实例的集合即可:sealed class NavigationItem(val route: String, val icon: Int, val title: String, val color: String) { object Home : NavigationItem("home", R.drawable.ic_home, "Home", "#FFFF00") object Music : NavigationItem("music", R.drawable.ic_music, "Music", "#FF00FF") object Movies : NavigationItem("movies", R.drawable.ic_movie, "Movies", "#00FFFF") object Books : NavigationItem("books", R.drawable.ic_book, "Books", "#FFAAAA") object Profile : NavigationItem("profile", R.drawable.ic_profile, "Profile", "#AAAAFF") companion object { val allItems = listOf(Home, Music, Movies, Books, Profile) } }遍历时直接调用
NavigationItem.allItems.forEach { /* 处理每个实例 */ }即可,没有反射开销,也不会出现依赖缺失问题。唯一需要注意的是新增子类时,要同步把实例加到allItems列表中。方案2:改用枚举类实现
如果场景不需要密封类支持多实例、复杂继承结构的特性,用枚举类是更省心的选择,枚举天生自带values()方法获取所有实例,不需要手动维护列表:enum class NavigationItem(val route: String, val icon: Int, val title: String, val color: String) { Home("home", R.drawable.ic_home, "Home", "#FFFF00"), Music("music", R.drawable.ic_music, "Music", "#FF00FF"), Movies("movies", R.drawable.ic_movie, "Movies", "#00FFFF"), Books("books", R.drawable.ic_book, "Books", "#FFAAAA"), Profile("profile", R.drawable.ic_profile, "Profile", "#AAAAFF") } // 遍历方式 NavigationItem.values().forEach { /* 处理每个实例 */ }枚举的局限性在于只能实现接口,不能继承其他类,所有实例默认都是单例,无法自定义子类的特殊继承逻辑,可以根据业务场景选择。
补充:原反射方案修复方式
如果一定要用sealedSubclasses的写法,不需要调整业务代码,只需要在对应模块的构建配置中引入Kotlin反射依赖即可,但这种方案启动时存在反射遍历的额外开销,依赖体积也会增大,非必要不推荐。
内容的提问来源于stack exchange,提问作者Elye
相关产品推荐
相关产品推荐

