基于参数类型的Kotlin返回类型推断

oknrviil  于 2023-05-29  发布在  Kotlin
关注(0)|答案(3)|浏览(359)

我有一个函数:

object CliUtils {
    var skipOptionalArgs = false

    fun readOptional(variableName: String, defaultValue: String? = null): String? {
        if (skipOptionalArgs) return defaultValue
        print("Provide the $variableName (optional${if (defaultValue != null) ", default: $defaultValue" else ""}): ")
        return readln().takeIf { it.isNotEmpty() } ?: defaultValue
    }
}

当我像这样使用这个函数时…

val inputName = readOptional("input name", "Input")

inputName的类型是String?。由于我给出的"Input"defaultValue,我知道变量不是null
我可以用一个双感叹号,像这样:

val inputName = readOptional("input name", "Input")!!

但是我觉得应该有一种方法来定义readOptional函数,这样就可以推断出String的返回类型,而不需要使用双感叹号。我想到了契约,但据我所知,契约只能引用参数,而不能引用返回类型。我还想到了将defaultValue参数类型的类型链接到返回类型的泛型,但我无法让它与其空性一起工作。
有没有办法让Kotlin明白如果defaultValue != null返回值也不是null?

xdnvmnnf

xdnvmnnf1#

你可以使用泛型来实现,但为了安全起见,我认为你必须为空默认版本提供一个重载。

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)
x6h2sr28

x6h2sr282#

这是可能的泛型:

@Suppress("UNCHECKED_CAST")
fun <T : String?> readOptional(variableName: String, defaultValue: T = null as 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
}

调用方不再需要邪恶的!!检查:

val inputName: String = readOptional("input name", "Input")

val inputNameNullable: String? = readOptional("input name", null)

val inputNameDefaultNullable: String? = readOptional("input name")

但我们必须付出的代价是未经检查的演员阵容。这是因为编译器无法确保方法在每个条件下都返回非空默认参数。
因此,我们让调用者站点可以轻松地使用所有变体,而无需额外的检查。但是正如前面提到的,编译器不能阻止我们错误地使用它。仍然不可能额外设置(错误的)泛型类型,或者让它从瓦尔类型推断错误的类型:

val inputNameDefaultTypedWrong: String = readOptional<String>("input name")
val inputNameDefaultNullableWrongInferred: String = readOptional("input name")

编辑:也可以结合一个额外的方法重载来解决,如@Tenfour04提到的:

fun readOptional(variableName: String): String? = readOptional(variableName, null)

我的想法和更多的解释也可以在这里找到:Kotlin generics with nullable types

vawmfj5a

vawmfj5a3#

另一个选项是重载函数:

fun readOptional(variableName: String, defaultValue: String) =
    readOptional(variableName, defaultValue as String?)!!

相关问题