(译)Kotlin 之 Nothing

Kotlin 中的 Nothing 到底有什么作用?

原文见Kotlin’s Nothing: Its Usefulness in Generics

本文介绍 Kotlin 中 Nothing 类型在泛型中的作用。先来看一个关于链表的具体例子。

这个链表封装了某种类型,不妨称为 T。链表可以是以下任意一种:

  • 类型一 - Node<T>。它包含两个属性, T 类型的 payload 和 LinkedList<T> 类型的 next
  • 类型二 - 一个空链表 EmptyList

使用 sealed class 用于保证链表要么是类型一要么是类型二。

所以可以写出如下代码:

1
2
3
4
sealed class LinkedList<out T>  {

data class Node<T>(val payload: T, var next: LinkedList<T> ) : LinkedList<T>()
}

如何写空链表有点挑战。考虑到所有的空链表都是一样的,所以空链表由 Kotlin 中的 object 表示。此外,空链表还必须是 LinkedList<T> 的子类。可以尝试如下写法:

1
2
3
4
5
6
7
sealed class LinkedList<out T>  {

data class Node<T>(val payload: T, var next: LinkedList<T> ) : LinkedList<T>()

object EmptyList<T> : LinkedList<T>() // won't compile

}

Kotlin 中的 object 不能带类型参数,所以上面代码编译失败。再来尝试去掉空链表的类型参数。

1
2
3
4
5
6
7
sealed class LinkedList<out T>  {

data class Node<T>(val payload: T, var next: LinkedList<T> ) : LinkedList<T>()

object EmptyList : LinkedList<T>() // won't compile

}

代码仍然编译失败。第5行代码中的 T 无法解析。必须为这里的 T 规定一个具体的类型。

见文章开头的那张图,TNode.payload 属性的类型。而空链表并不包含任何 Node。所以正确的代码如下:

1
2
3
4
5
6
7
8
9
sealed class LinkedList<out T>  {

data class Node<T>(val payload: T, var next: LinkedList<T> = EmptyList) : LinkedList<T>()

object EmptyList : LinkedList<Nothing>()

}

val nonEmptyList = LinkedList.Node(payload = "A", next = LinkedList.Node(payload = "B"))

Kotlin 中 Nothing 类型到底是什么?在 Kotlin REPL 中运行 println(Nothing::class.java) 命令输出结果如下:

1
2
println(Nothing::class.java)
class java.lang.Void

Kotlin 的 Nothing 其实就是 Java 中的 Void。在 Kotlin 中,Nothing 表示缺少类型。

再来看看为什么不能让 Kotlin 中的函数返回 Nothing?。比如以下代码编译出错:

1
fun getNothing() = Nothing() // won't compile

Kotlin 不允许实例化 NothingNothing 的构造方法是私有的。看看 Java 版本的 getNothing():

1
2
3
4
5
6
public class GetVoidExample {

public Void getVoid() {
return new Void(); // won't compile
}
}

Java 中 Void 的构造方法也是私有的。Void 同样不能被实例化。我们也不能返回 Void。所以这样看来 Kotlin 中不能返回 Void 是合理的。

总结一下: Kotlin 的 Nothing 其实就是 Java 中的 Void。Kotlin 中 Nothing 用于泛型,表示该泛型不包含任何类型信息,即该类型缺失。