如何让Kotlin根据defaultValue非空推断readOptional返回非空类型?
解决Kotlin readOptional函数的空类型推断问题
要实现当传入非空defaultValue时,函数返回非空类型、无需手动用!!强制转换的需求,可以通过泛型绑定返回值与参数类型的方式解决,具体实现如下:
优化后的函数实现
object CliUtils { var skipOptionalArgs = false fun <T : Any?> readOptional(variableName: String, defaultValue: T): T { if (skipOptionalArgs) return defaultValue val prompt = buildString { append("Provide the $variableName (optional") defaultValue?.let { append(", default: $it") } append("): ") } print(prompt) return readln().takeIf { it.isNotEmpty() } as T? ?: defaultValue } // 为无默认值的场景提供重载 fun readOptional(variableName: String): String? { if (skipOptionalArgs) return null print("Provide the $variableName (optional): ") return readln().takeIf { it.isNotEmpty() } } }
效果说明
- 传入非空默认值时,返回类型自动推断为非空:
// inputName 类型为 String,而非 String? val inputName = CliUtils.readOptional("input name", "Input") - 传入
null作为默认值,或调用无参数重载时,返回类型为String?:// inputName 类型为 String? val inputName = CliUtils.readOptional("input name", null) val optionalInput = CliUtils.readOptional("optional input")
原理说明
原函数返回类型固定为String?,编译器无法根据defaultValue的非空性动态调整返回类型。而泛型版本将返回类型T与defaultValue的类型绑定:
- 若
defaultValue是非空String,则T被推断为String,函数返回非空值; - 若
defaultValue是null(即类型为String?),则T被推断为String?,函数返回可空值。
关于契约的补充说明
Kotlin的契约(Contracts)目前仅支持有限的断言逻辑,无法直接声明"当defaultValue非空时返回值必非空"这类条件式关联,因此泛型方案是更稳定可靠的选择。若一定要尝试契约,需启用@ExperimentalContracts注解,但无法完全替代泛型的类型绑定效果。
内容的提问来源于stack exchange,提问作者Rik Schaaf
相关产品推荐
相关产品推荐

