科特林有三种性质非常相似的类型:
Void
Unit
Nothing
似乎他们在犯JavaScript错误:
null
undefined
void(0)
假设它们没有犯同样的错误,它们的作用是什么?它们有什么不同?
答案 0 :(得分:10)
Void
类型来自Java。除非您正在使用使用它的Java库,否则通常不会从Kotlin使用它。
Unit
类型是您从不返回任何感兴趣内容的函数中返回的内容。这种功能通常会产生某种副作用。单位类型只有一个可能的值,即Unit.VALUE
。在Java中使用Unit
(小写v)时,可以在Kotlin中将void
用作返回类型。
Nothing
类型没有值。如果函数的返回类型为Nothing
,则它不能正常返回。它要么引发异常,要么进入无限循环。调用返回类型为Nothing
的函数之后的代码将被Kotlin编译器标记为不可访问。
由于Nothing
没有值,因此Nothing?
实际上是在Kotlin中仅捕获null
值的类型。
答案 1 :(得分:4)
答案 2 :(得分:4)
Unit
Unit
就像void
在Kotlin中,当函数不返回任何有意义的值时,就声明它返回Unit
,就像Java中的void
:
fun greet(): Unit { println("Good day!") }
由于函数Unit
被编译器视为默认返回类型,因此在函数返回Unit
时跳过写Unit
的约定:
fun greet() { println("Good day!") }
Unit
是单例
Unit
是一个只有单个对象(单例模式)的类,而该对象就是Unit
本身。在kotlin
包中使用对象声明对其进行声明,如下所示:
public object Unit {
override fun toString() = "kotlin.Unit"
}
Unit
在函数式编程中
Kotlin对函数式编程提供了一流的支持。在功能编程语言中使用Unit
是很常见的。通过使所有函数都声明为具有返回值,即使函数不返回值,也可以使函数类型更易读:
val greet: () -> Unit = { println("Good day!") }
在这里,() -> Unit
是一种函数类型,Unit
之后的->
表示此函数类型不返回任何有意义的值。在功能类型中不能提及Unit
。
Unit
用于扩展泛型
每个函数都必须返回一个值。 Kotlin决定用 class 表示它,而不是像Java中那样用特殊类型void
表示。使用类的原因是,通过使类成为类型层次结构的一部分,可以使类型系统更加一致。
例如,假设我们有一个名为interface
的通用Worker<T>
,它可以执行一些工作。该接口的doWork()
函数可以完成一些工作,并且必须返回一个值 T
:
interface Worker<T> {
fun doWork(): T
}
但是有时,我们可能希望将此接口用于某些不需要返回任何值的工作 ,例如,LogWorker
类中的日志工作如下所示扩展了Worker
界面:
class LogWorker : Worker<Unit> {
override fun doWork() {
// Do the logging
}
}
这是Unit
的魅力所在,我们可以使用原来设计为返回值的预先存在的接口。在这里,我们使doWork()
函数返回Unit
值以实现没有任何返回值的目的。 因此,当您覆盖返回通用参数的函数时,这很有用。
请注意,我们也忽略了Unit
函数的doWork()
返回类型。也无需编写return
语句。
Nothing
Nothing
的价值永不存在
在Kotlin中,类Nothing
表示一个不存在的值。此类永远不会有任何值/对象,因为其constructor
被保留为private
。它在kotlin
包中的定义如下:
public class Nothing private constructor()
Nothing
用于永远不返回值的函数的返回类型。例如,具有无限循环的函数或总是抛出异常的函数。 Kotlin标准库中的error()
函数是一个始终抛出异常并返回Nothing
的示例。这是它的代码:
fun error(message: Any): Nothing = throw IllegalStateException(message.toString())
Nothing
是底部类型
在函数式编程中,没有值的类型称为底部类型,它是所有其他类型的子类型。因此,Nothing
是Kotlin中所有类型的子类型,就像Any
是所有类型的超类型一样。因此,类型Nothing
的值(永不存在)可分配给所有类型的变量,例如:
val user: User = request.user ?: error("User not found")
在这里,如果error()
是user
,则使用elvis运算符(null
)调用上面定义的?:
函数。 error()
函数返回类型Nothing
的值,但是可以将其分配给类型User
的变量,因为Nothing
是User
的子类型,就像它是任何其他类型的子类型。编译器允许这样做,因为它知道error()
函数永远不会返回值,因此没有危害。
类似地,您可以从具有任何其他返回类型的函数中返回Nothing
:
fun getUser(request: Request): User {
return request.user ?: error("User not found")
}
此处,即使getUser()
函数声明为返回User
,但如果Nothing
为user
,它也可能返回null
。
Nothing
(在空对象模式中)
考虑以下删除列表中给定文件的函数示例:
fun deleteFiles(files: List<File>? = null) {
if (files != null) files.forEach { it.delete() }
}
此函数设计的问题在于,它无法传达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
...
}
现在,我们在deleteFiles()
函数中使用此空对象作为参数的默认值:
fun deleteFiles(files: List<File> = emptyList()) {
files.forEach { it.delete() }
}
这消除了null
或为空的不确定性,并使意图更清晰。它还删除了空检查,因为空对象上的函数为空,它们将被调用,但它们为 no-ops (其中没有任何操作,因此它们将不执行任何操作)。
Nothing
(针对协变泛型)
在上面的示例中,编译器允许我们在期望List<Nothing>
的地方传递List<File>
。这是因为Kotlin中的List
接口是协变的,因为它是使用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
包,而Unit
和Nothing
来自kotlin
包。 Void
不能在Kotlin中使用。 Kotlin以Unit
的形式有自己的课程。
Void
在Java中用于扩展通用接口,例如我们为Worker
编写的Unit
接口示例,其中我们必须返回一个值。因此,要将Kotlin代码转换为Java,我们可以像在Void
示例中使用Unit
一样使用Worker
,并按如下所示用Java重写代码:
interface Worker<T> {
T doWork();
}
class LogWorker implements Worker<Void> {
@Override public Void doWork() {
// Do the logging
return null;
}
}
请注意,使用Void
时,我们必须使用Void
作为返回类型(不能跳过),并且需要编写return
语句,而对于{{1 }}我们都可以跳过。这是避免在Kotlin代码中使用Unit
的另一个原因。
结论
因此,我认为Void
和Unit
并不是Kotlin设计师的错误,也没有像Nothing
,null
和undefined
Javascript。 void(0)
和Unit
使函数式编程变得轻而易举,同时还提供了上述其他有用的功能。它们在其他函数式编程语言中也很常见。
就是这样!希望有帮助。