Scala 中 Null/Nothing/Unit 的使用

我刚读到: http://oldfashionedsoftware.com/2008/08/20/a-post-about-nothing/

据我所知,Null是一个 trait,它的唯一实例是 null

当一个方法接受一个 Null 参数时,我们只能直接传递一个 Null引用或者 null,而不能传递任何其他引用,即使它是 Null (例如 nullString: String = null)。

我只是想知道在哪些情况下使用这种 Null特性可能是有用的。 还有一个 Nothing 特征,我没有看到更多的例子。


我也不知道 Nothing 和 Unit 作为返回类型有什么区别,因为两者都不返回任何结果,例如,当我有一个执行日志记录的方法时,如何知道使用哪一个?


除了返回类型之外,Unit/Null/Nothing 还有其他用法吗?

64520 次浏览

You only use Nothing if the method never returns (meaning it cannot complete normally by returning, it could throw an exception). Nothing is never instantiated and is there for the benefit of the type system (to quote James Iry: "The reason Scala has a bottom type is tied to its ability to express variance in type parameters."). From the article you linked to:

One other use of Nothing is as a return type for methods that never return. It makes sense if you think about it. If a method’s return type is Nothing, and there exists absolutely no instance of Nothing, then such a method must never return.

Your logging method would return Unit. There is a value Unit so it can actually be returned. From the API docs:

Unit is a subtype of scala.AnyVal. There is only one value of type Unit, (), and it is not represented by any object in the underlying runtime system. A method with return type Unit is analogous to a Java method which is declared void.

I've never actually used the Null type, but you use Unit, where you would on java use void. Nothing is a special type, because as Nathan already mentioned, there can be no instance of Nothing. Nothing is a so called bottom-type, which means, that it is a sub-type of any other type. This (and the contravariant type parameter) is why you can prepend any value to Nil - which is a List[Nothing] - and the list will then be of this elements type. None also if of type Option[Nothing]. Every attempt to access the values inside such a container will throw an exception, because that it the only valid way to return from a method of type Nothing.

The article you quote can be misleading. The Null type is there for compatibility with the Java virtual machine, and Java in particular.

We must consider that Scala:

  • is completely object oriented: every value is an object
  • is strongly typed: every value must have a type
  • needs to handle null references to access, for example, Java libraries and code

thus it becomes necessary to define a type for the null value, which is the Null trait, and has null as its only instance.

There is nothing especially useful in the Null type unless you're the type-system or you're developing on the compiler. In particular I can't see any sensible reason to define a Null type parameter for a method, since you can't pass anything but null

Do you have usages of Unit / Null / Nothing as something else than a return type?


Unit can be used like this:

def execute(code: => Unit):Unit = {
// do something before
code
// do something after
}

This allows you to pass in an arbitrary block of code to be executed.


Null might be used as a bottom type for any value that is nullable. An example is this:

implicit def zeroNull[B >: Null] =
new Zero[B] { def apply = null }

Nothing is used in the definition of None

object None extends Option[Nothing]

This allows you to assign a None to any type of Option because Nothing 'extends' everything.

val x:Option[String] = None

if you use Nothing, there is no things to do (include print console) if you do something, use output type Unit

object Run extends App {
//def sayHello(): Nothing = println("hello?")
def sayHello(): Unit = println("hello?")
sayHello()
}

... then how to use Nothing?

trait Option[E]
case class Some[E](value: E) extends Option[E]
case object None extends Option[Nothing]

Nothing is often used implicitly. In the code below, val b: Boolean = if (1 > 2) false else throw new RuntimeException("error") the else clause is of type Nothing, which is a subclass of Boolean (as well as any other AnyVal). Thus, the whole assignment is valid to the compiler, although the else clause does not really return anything.

Here's an example of Nothing from scala.predef:

  def ??? : Nothing = throw new NotImplementedError

In case you're unfamiliar (and search engines can't search on it) ??? is Scala's placeholder function for anything that hasn't been implemented yet. Just like Kotlin's TODO.

You can use the same trick when creating mock objects: override unused methods with a custom notUsed method. The advantage of not using ??? is that you won't get compile warnings for things you never intend to implement.

In terms of category theory Nothing is an initial object and Unit is a terminal object.

https://en.wikipedia.org/wiki/Initial_and_terminal_objects

Initial objects are also called coterminal or universal, and terminal objects are also called final.

If an object is both initial and terminal, it is called a zero object or null object.