如何实现Kotlin中readForUsage函数基于双参数的返回类型推断?
Kotlin:基于Usage枚举与默认值自动推断readForUsage返回类型
需求说明
希望readForUsage函数的返回值遵循以下规则自动推断:
- 传入
Usage.REQUIRED时,返回非空String类型; - 传入
Usage.UNSUPPORTED时,返回可空String?类型; - 传入
Usage.OPTIONAL时,返回类型由defaultValue参数决定(可为String或String?,与readOptional逻辑一致)。
当前实现无法让返回值同时依赖usage枚举和defaultValue的类型,导致test()函数中多个调用无法编译。
原始代码
enum class Usage { REQUIRED, OPTIONAL, UNSUPPORTED } object CliUtils { var skipOptionalArgs = false fun test() { val readForUsageRequired1: String = readForUsage("var1", Usage.REQUIRED) // 无法编译 val readForUsageOptional1: String? = readForUsage("var2", Usage.OPTIONAL) val readForUsageUnsupported1: String? = readForUsage("var3", Usage.UNSUPPORTED) val readForUsageRequired2: String = readForUsage("var1", Usage.REQUIRED, null as String?) // 无法编译 val readForUsageOptional2: String? = readForUsage("var2", Usage.OPTIONAL, null as String?) val readForUsageUnsupported2: String? = readForUsage("var3", Usage.UNSUPPORTED, null as String?) val readForUsageRequired3: String = readForUsage("var1", Usage.REQUIRED, "default1") // 无法编译 val readForUsageOptional3: String = readForUsage("var2", Usage.OPTIONAL, "default2") // 无法编译 val readForUsageUnsupported3: String? = readForUsage("var3", Usage.UNSUPPORTED, "default3") } @Suppress("UNCHECKED_CAST") fun <T: String?> readForUsage(variableName: String, usage: Usage, defaultValue: T) = when (usage) { Usage.REQUIRED -> readRequired(variableName) as T Usage.OPTIONAL -> readOptional(variableName, defaultValue) Usage.UNSUPPORTED -> null } fun readForUsage(variableName: String, usage: Usage): String? = readForUsage(variableName, usage, null as String?) fun <T: String?> readOptional(variableName: String, defaultValue: T): T { if (skipOptionalArgs) return defaultValue print("Provide the $variableName (optional${if (defaultValue != null) ", default: $defaultValue" else ""}): ") @Suppress("UNCHECKED_CAST") return (readln().takeIf { it.isNotEmpty() } ?: defaultValue) as T } fun readOptional(variableName: String): String? = readOptional(variableName, null) fun readRequired(variableName: String): String { while (true) { print("Provide the $variableName: ") val value = readln() if (value.isNotEmpty()) { return value } println("Invalid $variableName provided!") } } }
解决方案
要实现基于usage和defaultValue的联合类型推断,需利用Kotlin重载函数+枚举类型细化特性,为不同Usage分支提供针对性重载,让编译器精准推断返回类型:
改写后的代码
enum class Usage { REQUIRED, OPTIONAL, UNSUPPORTED } object CliUtils { var skipOptionalArgs = false fun test() { val readForUsageRequired1: String = readForUsage("var1", Usage.REQUIRED) // 正常编译 val readForUsageOptional1: String? = readForUsage("var2", Usage.OPTIONAL) // 正常编译 val readForUsageUnsupported1: String? = readForUsage("var3", Usage.UNSUPPORTED) // 正常编译 val readForUsageRequired2: String = readForUsage("var1", Usage.REQUIRED, null as String?) // 正常编译 val readForUsageOptional2: String? = readForUsage("var2", Usage.OPTIONAL, null as String?) // 正常编译 val readForUsageUnsupported2: String? = readForUsage("var3", Usage.UNSUPPORTED, null as String?) // 正常编译 val readForUsageRequired3: String = readForUsage("var1", Usage.REQUIRED, "default1") // 正常编译 val readForUsageOptional3: String = readForUsage("var2", Usage.OPTIONAL, "default2") // 正常编译 val readForUsageUnsupported3: String? = readForUsage("var3", Usage.UNSUPPORTED, "default3") // 正常编译 } // 针对Usage.REQUIRED的重载:返回非空String,忽略defaultValue fun readForUsage(variableName: String, usage: Usage.REQUIRED): String = readRequired(variableName) fun readForUsage(variableName: String, usage: Usage.REQUIRED, defaultValue: String?): String = readRequired(variableName) // 针对Usage.OPTIONAL的重载:返回类型由defaultValue决定 fun <T : String?> readForUsage(variableName: String, usage: Usage.OPTIONAL, defaultValue: T): T = readOptional(variableName, defaultValue) fun readForUsage(variableName: String, usage: Usage.OPTIONAL): String? = readOptional(variableName) // 针对Usage.UNSUPPORTED的重载:返回String? fun readForUsage(variableName: String, usage: Usage.UNSUPPORTED): String? = null fun readForUsage(variableName: String, usage: Usage.UNSUPPORTED, defaultValue: String?): String? = null fun <T : String?> readOptional(variableName: String, defaultValue: T): T { if (skipOptionalArgs) return defaultValue print("Provide the $variableName (optional${if (defaultValue != null) ", default: $defaultValue" else ""}): ") return (readln().takeIf { it.isNotEmpty() } ?: defaultValue) as T } fun readOptional(variableName: String): String? = readOptional(variableName, null) fun readRequired(variableName: String): String { while (true) { print("Provide the $variableName: ") val value = readln() if (value.isNotEmpty()) { return value } println("Invalid $variableName provided!") } } }
关键改动说明
- 按枚举分支重载函数:为每个
Usage常量单独定义重载,利用Kotlin对枚举常量的类型细化特性,让编译器根据传入的usage值精准匹配函数,确定返回类型。 - 明确各分支返回规则:
Usage.REQUIRED分支强制返回非空String,符合必填参数逻辑;Usage.OPTIONAL分支复用readOptional的泛型逻辑,返回类型完全由defaultValue决定;Usage.UNSUPPORTED分支直接返回null,对应String?类型。
- 移除不安全类型转换:原实现中的
as T强制转换被移除,类型推断由编译器保证,避免运行时安全隐患。
内容的提问来源于stack exchange,提问作者Rik Schaaf
相关产品推荐
相关产品推荐

