首页 文章

如果参数不为null,如何告诉kotlin函数不返回null?

提问于
浏览
4

我想编写一个便利扩展来从Map中提取值,同时解析它们 . 如果解析失败,则该函数应返回默认值 . 这一切都很好,但我想告诉Kotlin编译器,当默认值不为null时,结果也不会为null . 我可以通过 @Contract 注释在Java中使用它,但它似乎在Kotlin中不起作用 . 可以这样做吗? Contract 不适用于扩展功能吗?这是kotlin的尝试:

import org.jetbrains.annotations.Contract

private const val TAG = "ParseExtensions"

@Contract("_, !null -> !null")
fun Map<String, String>.optLong(key: String, default: Long?): Long? {
    val value = get(key)
    value ?: return default

    return try {
        java.lang.Long.valueOf(value)
    } catch (e: NumberFormatException) {
        Log.e(TAG, e)
        Log.d(TAG, "Couldn't convert $value to long for key $key")

        default
    }
}

fun test() {
    val a = HashMap<String, String>()

    val something: Long = a.optLong("somekey", 1)
}

在上面的代码中,IDE将突出显示 something 赋值中的错误,尽管使用非null默认值1调用 optLong . 为了进行比较,这里是类似的代码,它通过Java中的注释和 Contract 测试可空性:

public class StackoverflowQuestion
{
    @Contract("_, !null -> !null")
    static @Nullable Long getLong(@NonNull String key, @Nullable Long def)
    {
        // Just for testing, no real code here.
        return 0L;
    }

    static void testNull(@NonNull Long value) {
    }

    static void test()
    {
        final Long something = getLong("somekey", 1L);
        testNull(something);
    }
}

上面的代码没有显示任何错误 . 仅当删除了 @Contract 注释时,IDE才会警告对 testNull() 的调用可能为空值 .

2 回答

  • 1

    您可以通过使该函数通用来完成此操作 .

    fun <T: Long?> Map<String, String>.optLong(key: String, default: T): T 
    {
        // do something.
        return default
    }
    

    哪个可以这样使用:

    fun main(args: Array<String>) {
        val nullable: Long? = 0L
        val notNullable: Long = 0L
    
        someMap.optLong(nullable) // Returns type `Long?`
        someMap.optLong(notNullable) // Returns type `Long`
    }
    

    这是有效的,因为 Long?Long 的超类型 . 通常会推断出类型,以便根据参数返回可空或不可空类型 .

    这将“告诉Kotlin编译器,当默认值不为null时,结果也不会为null . ”

  • 1

    很遗憾你不能在Kotlin 1.2或更低版本中这样做 .

    然而,Kotlin正在开发 contract dsl 尚未通知,这是不可用的ATM(因为它们在stdlib中被声明为 internal )但你可以使用一些黑客在你的代码中使用它们(通过自己编译stdlib,使所有他们公开) .

    您可以在stdlib ATM中看到它们:

    @kotlin.internal.InlineOnly
    public inline fun <T, R> T.let(block: (T) -> R): R {
        contract {
            callsInPlace(block, InvocationKind.EXACTLY_ONCE)
        }
        return block(this)
    }
    

    也许会有类似的东西

    contract {
       when(null != default) implies (returnValue != null)
    }
    

    将来可以解决您的问题 .

    解决方法

    就个人而言,我建议你用NotNull Long 替换 default 的类型并将其称为

    val nullableLong = blabla
    val result = nullableLong?.let { oraora.optLong(mudamuda, it) }
    

    resultLong? ,仅当 nullableLong 为空时才为空 .

相关问题