如何在 Kotlin 中检查 List 是否包含 null?

4

我不太理解为什么会出现这样的问题。

class Main {
    private val outputStreams: List<OutputStream>

    @JvmOverloads constructor(outputStreams: List<OutputStream> = LinkedList()) {
        if(outputStreams.contains(null)) {
            throw IllegalArgumentException("outputStreams mustn't contain null")
        }
        this.outputStreams = outputStreams
    }
}

导致编译错误的原因是...Main.kt:[12,26] 类型推断失败。类型参数T的值应该在输入类型(参数类型、接收器类型或期望类型)中提到。尝试显式地指定它。如果我使用outputStreams.contains(null as OutputStream)),则编译成功,但是Main(LinkedList<OutputStream>()) 在运行时失败了。
kotlin.TypeCastException: null cannot be cast to non-null type java.io.OutputStream
    at kotlinn.collection.contains.nulll.check.Main.<init>(Main.kt:12)
    at kotlinn.collection.contains.nulll.check.MainTest.testInit(MainTest.kt:13)

这使我别无选择,只能尽可能保持代码与原始Java代码的相似性,这也是我的意图,理解问题本身,而不是寻找替代方法。


尝试使用 @JvmOverloads constructor(outputStreams: List<OutputStream?> = LinkedList())。通过在问号后面添加符号,可以告诉编译器列表可能包含一个空值。 - Rado
3个回答

4
对于编译器来说,参数outputStreams的类型是List<OutputStream>而不是List<OutputStream?>,因此不能包含null。类型系统不允许在此列表中包含null,因此无需检查它。
另一方面,如果该参数实际上可能包含null(因为它来自Java调用者),则应明确将其标记为可为空:List<OutputStream?>

哇,Kotlin 有什么不能尽可能智能地处理的吗? - Kalle Richter
不是下投票者,但我想补充一点,鉴于 @JvmOverloads 注解,这很可能是从 Java 中使用的,因此技术上类型系统不能再保证列表不包含 null。如果你在 Java 中使用 Collections.<OutputStream>singletonList(null) 实例化它,它仍然可以编译,但在运行时会失败。如果你知道它将从 Java 中使用,你可以遵循 @ToraCode 的建议并将类型声明为 List<OutputStream?>,但前提是你确实需要特定的 IllegalArgumentException。否则,我只会允许 NPE 被抛出并避免歧义。 - Kevin Coppock

3
我相信答案是List<OutputStream?>?将使您的列表可以包含null。 查看文档:点击此处

1

filterNotNull()方法可以从集合中过滤掉所有的null对象,因此您可以使用以下代码。 显然,您需要告诉编译器集合中的对象是可为空的。 我将OutputStream类型更改为String,以便更轻松地进行测试。

    class Main{
private val outputStreams: List<String?>

@JvmOverloads constructor(outputStreams: List<String?> = LinkedList()) {

    if(outputStreams.filterNotNull().size < outputStreams.size) {
        throw IllegalArgumentException("outputStreams mustn't contain null")
    }
    this.outputStreams = outputStreams
}

fun getOutputStream(): List<String?>{
    return outputStreams
}
 }

第二种方法是使用 let。T.let(block: (T) -> R): R,您可以接受可空对象,但需要检查是否有任何“null”字符串,并相应地做出反应。
    class Main{
lateinit var outputStreams: List<String?>

@JvmOverloads constructor(outputStreams: List<String?> = LinkedList()) {

    outputStreams.let {
        this.outputStreams = outputStreams
    }

}

fun getOutputStream(): List<String?>{
    return outputStreams
}
}

    fun main(args: Array<String>) {
val outputStreamWithNull: List<String?> = listOf("alpha", "beta", null, "omega")
val mainA = Main(outputStreamWithNull)


mainA.getOutputStream().forEach {
    println(it)
}

}

如果您“确信”构造函数中的对象参数应该是非空对象的集合,则可以删除? 但是,对于List集合的可为空检查必须由初始化Main类的对象执行,因此您基本上将NullPointerException捕获的责任移交给其他人。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接