Kotlin-空虚vs.单位vs.无

ser*_*0ne 14 kotlin

Kotlin具有三种性质非常相似的类型:

  • Void
  • Unit
  • Nothing

似乎他们在犯JavaScript错误:

  • null
  • undefined
  • void(0)

假设他们没有陷入相同的错误中,它们的全部作用是什么,它们又有何不同?

Yog*_*ity 40


Unit


Unit 就好像 void

在 Kotlin 中,当一个函数没有返回任何有意义的值时,它被声明为 return Unit,就像void在 Java 中一样:

fun greet(): Unit { println("Good day!") }
Run Code Online (Sandbox Code Playgroud)

Unit当函数返回时跳过写入是一种约定,Unit因为Unit编译器认为它是默认返回类型:

fun greet() { println("Good day!") }
Run Code Online (Sandbox Code Playgroud)

Unit 是单身人士

TheUnit是一个只有一个对象(单例模式)的类,该对象就是它Unit本身。它kotlin使用对象声明在包中声明,如下所示:

public object Unit {
    override fun toString() = "kotlin.Unit"
}
Run Code Online (Sandbox Code Playgroud)

Unit 在函数式编程

Kotlin 对函数式编程有一流的支持。Unit在函数式编程语言中使用 a是很常见的。通过使所有函数都声明为具有返回值,即使函数没有返回值,它也使函数类型更具可读性:

val greet: () -> Unit = { println("Good day!") }
Run Code Online (Sandbox Code Playgroud)

这里,() -> Unit是一个函数类型,Unit后面的->表示这个函数类型不返回任何有意义的值。提到Unit不能在函数类型中跳过。


Unit 用于扩展泛型

每个函数都必须返回一个值。Kotlin 决定用一个来表示它,而不是void像在 Java 中那样用特殊类型来表示。使用类的原因是通过将其作为类型层次结构的一部分,可以使类型系统更加一致。

例如,假设我们有一个执行一些工作的泛型interface调用Worker<T>doWork()这个接口的函数做了一些工作并且必须返回一个值 T

interface Worker<T> {
    fun doWork(): T
}
Run Code Online (Sandbox Code Playgroud)

但有时,我们可能希望将这个接口用于一些不需要返回任何值的工作,例如日志记录的工作,在LogWorker如下所示的扩展Worker接口的类中:

class LogWorker : Worker<Unit> {
    override fun doWork() {
        // Do the logging
    }
}
Run Code Online (Sandbox Code Playgroud)

这是Unit我们能够使用最初设计为返回值的预先存在的接口的神奇之处。在这里,我们让doWork()函数返回Unit值来满足我们没有任何返回值的目的。因此,当您覆盖返回泛型参数的函数时,它很有用。

请注意,我们还跳过了提及函数的Unit返回类型doWork()。也没有必要写return声明。


Nothing


Nothing的价值从不存在

在 Kotlin 中,类Nothing代表一个从不存在的值。这个类永远不会有任何值/对象,因为它 constructor 被保留private。它在kotlin包中定义如下:

public class Nothing private constructor()
Run Code Online (Sandbox Code Playgroud)

Nothing用于从不返回值的函数的返回类型。例如,具有无限循环的函数或始终抛出异常的函数。在error()从科特林标准库函数是总是抛出一个异常,并返回一个例子Nothing。这是它的代码:

fun error(message: Any): Nothing = throw IllegalStateException(message.toString())
Run Code Online (Sandbox Code Playgroud)

Nothing 是底部类型

在函数式编程中,没有值的类型称为底层类型,它是所有其他类型的子类型。所以,Nothing是Kotlin 中所有类型的类型,就像所有类型Any?类型一样。因此,类型的值(从不存在)Nothing可以分配给所有类型的变量,例如:

val user: User = request.user ?: error("User not found")
Run Code Online (Sandbox Code Playgroud)

在这里,我们使用 elvis operator( )调用error()我们之前定义的函数,如果user是。该函数返回类型的值,但它可被分配给类型的变量,因为是的一个子类型,就像它是任何其它类型的子类型。编译器允许这样做是因为它知道该函数永远不会返回值,因此没有任何危害。null?:error()NothingUserNothingUsererror()

同样,您可以Nothing从具有任何其他返回类型的函数返回:

fun getUser(request: Request): User {
    return request.user ?: error("User not found")
}
Run Code Online (Sandbox Code Playgroud)

在这里,即使getUser()函数被声明为返回 a User,它也可能返回Nothing,如果usernull


Nothing 在空对象模式中

考虑以下删除列表中给定文件的函数示例:

fun deleteFiles(files: List<File>? = null) {
    if (files != null) files.forEach { it.delete() }
}
Run Code Online (Sandbox Code Playgroud)

这个函数设计的问题在于它没有传达List<File>是空的null还是有元素的。另外,我们需要null在使用之前检查列表是否存在。

为了解决这个问题,我们使用空对象设计模式。在空对象模式中,我们不使用null引用来表达对象的缺失,而是使用一个实现了预期接口的对象,但将方法体留空。

所以,我们定义了接口的对象List<Nothing>

// This function is already defined in the Kotlin standard library
fun emptyList() = object : List<Nothing> {
    override fun iterator(): Iterator<Nothing> = EmptyIterator
    ...
}
Run Code Online (Sandbox Code Playgroud)

现在我们在deleteFiles()函数中使用这个空对象作为参数的默认值:

fun deleteFiles(files: List<File> = emptyList()) {
    files.forEach { it.delete() }
}
Run Code Online (Sandbox Code Playgroud)

这消除了null或空的不确定性,使意图更加清晰。它还删除了空检查,因为空对象上的函数是空的,它们将被调用但它们是无操作的(它们中没有操作,所以它们什么都不做)。


Nothing 对于协变泛型

在上面的例子中,编译器允许我们通过List<Nothing>在那里List<File>的预期。这是因为ListKotlin 中的接口是协变的,因为它是使用out关键字定义的,即List<out T>. 正如我们所了解的,Nothing是所有类型Nothing的子类型,也是 的子类型File。并且由于协方差,List<Nothing>List<File>List<Int>List<User>等等...的子类型List<AllTypes>。这适用于具有协变泛型( out) 的任何类型,而不仅仅是List.


Nothing 为了更好的表现

就像emptyList()我们示例中使用的函数一样emptyMap(),有一些预定义的函数,如, emptySet()emptySequence()返回空对象。所有这些都是使用Nothing. 您可以像这样定义自己的对象。

这里的好处是,这些返回单例对象,例如,你可以调用相同的emptyList()函数来获取一个空实例,无论是用于分配给List<File>List<Int>和......List<AllTypes>以及在多个地方。由于每次都返回同一个对象,因此节省了对象创建和内存分配的成本。


Void


Void 用于在 Java 中扩展泛型

Void类是从该java.lang封装,而UnitNothing来自kotlin包。Void不打算在 Kotlin 中使用。Kotlin 有自己的类,形式为Unit.

Void在 Java 中用于扩展通用接口,例如我们WorkerUnit必须返回值而编写的接口示例。因此,对于我们的科特林代码转换为Java中,我们可以用Void我们用同样的方式Unit为我们的Worker榜样,Java重写代码,如下所示:

interface Worker<T> {
    T doWork();
}

class LogWorker implements Worker<Void> {
    @Override public Void doWork() {
        // Do the logging
        return null;
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,当使用 时Void,我们必须Void用作返回类型(不能跳过)以及需要编写return语句,而 forUnit我们可以跳过两者。这是避免Void在 Kotlin 代码中使用的另一个原因。


结论

所以,UnitNothing是不是科特林设计师一个错误在我看来并没有为可疑的nullundefinedvoid(0)在Javascript。Unit并且Nothing使函数式编程变得轻而易举,同时提供中提到的其他实用功能。它们在其他函数式编程语言中也很常见。

就是这样!希望有帮助。


mar*_*ran 23

Void类型是从Java。除非您正在使用使用Java的Java库,否则通常不会从Kotlin使用它。

Unit类型是你从一个不返回任何感兴趣的函数返回的内容。这种功能通常会产生某种副作用。单位类型只有一个可能的值,即Unit.VALUE。您可以使用Unit在科特林返回类型时要使用void在Java中(小写V)。

Nothing类型没有值。如果一个函数具有return type Nothing,则它不能正常返回。它要么引发异常,要么进入无限循环。带有返回类型的函数调用之后的代码Nothing将被Kotlin编译器标记为不可访问。

因为Nothing没有值,Nothing?所以实际上是nullKotlin中仅捕获值的类型。


小智 6

Void 是普通的Java类,在Kotlin中没有特殊含义。

Unit替换了Java void(注意不是Void

Nothing是一个从未存在的值。如果抛出错误,则为Nothing。更多信息在这里

  • TypeScript 中的“Nothing”就像“never” (3认同)