Scala中Null / Nothing / Unit的用法


95

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

据我了解,Null是一个特征,唯一的实例是null

当一个方法接受一个Null参数时,即使它为null ,我们也只能将其传递给Null引用或null直接传递给它,而不能传递任何其他引用nullString: String = null

我只是想知道在哪些情况下使用此Null特征可能有用。还有Nothing特性,我没有再看到更多示例。


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


除返回类型外,是否还有单位/空/无其他用法?

Answers:


80

仅在方法永不返回时才使用Nothing(这意味着无法通过返回正常完成,它可能会引发异常)。什么都不会实例化,而且还有类型系统的好处(引用James Iry:“ Scala具有底部类型的原因与其表达类型参数差异的能力有关。”)。从您链接到的文章:

Nothing的另一种用途是用作永不返回的方法的返回类型。如果您考虑一下,这是有道理的。如果方法的返回类型为Nothing,并且绝对不存在Nothing的实例,则此类方法必须永不返回。

您的日志记录方法将返回Unit。有一个值单位,因此它实际上可以返回。从API文档

Unit是scala.AnyVal的子类型。只有一个类型为Unit()的值,并且底层运行系统中的任何对象均未表示该值。返回类型为Unit的方法类似于声明为void的Java方法。


2
谢谢,“永不回头”表示您的电话是无限期阻塞的(例如,作业调度程序的启动方法吗?)
Sebastien Lorber 2013年

3
@Sabastien:它不会正常返回,可能会引发异常(请参阅james-iry.blogspot.com/2009/08/…)。如果阻塞调用仅以抛出异常结束,则该计数将计数。感谢您的问题,这需要澄清。
内森·休斯

18

您引用的文章可能会引起误解。该Null类型用于与Java虚拟机(尤其是Java)兼容。

我们必须考虑Scala

  • 完全面向对象:每个值都是一个对象
  • 强类型化:每个值都必须具有一个类型
  • 需要处理null访问的引用,例如Java库和代码

因此,有必要为null值定义一个类型,它是Null特征,并且具有null唯一的实例。

Null除非您是类型系统或在编译器上进行开发,否则在类型中没有什么特别有用的。特别是,我看不到任何合理的理由来定义方法的Null类型参数,因为除了null


是的,所以最后没有其他使用Null的方法了吗?
Sebastien Lorber

@SebastienLorber编辑了答案。对于普通的开发人员,我实际上看不到任何用法。也许其他人可以想到一些有用的东西。
pagoda_5b

谢谢你 如果我们知道发生这种事情的原因,我们就会理解它们,否则我们会记住它们。
Sreekar '16

当您有类型参数并且可能希望像在此问答中那样返回null时,Null很有用,因为您不鼓励在scala中使用null,但这种情况很少出现,因此在类型系统中也可能有其他用法
Daniel Carlsson

15

除返回类型外,是否还有单位/空/无其他用法?


Unit 可以这样使用:

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

这使您可以传入要执行的任意代码块。


Null可能用作任何可为空的值的底部类型。一个例子是这样的:

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

Nothing 用于定义 None

object None extends Option[Nothing]

这允许您将a分配None给任何类型的,Option因为Nothing“扩展”了所有内容。

val x:Option[String] = None

好。在使用Unit时,您可能使用了通用类型,因此,如果您的代码块返回的不是unit,则execute可以返回此通用类型。
Sebastien Lorber

正如@drexin在对另一个答案的评论中所说的那样,它通常用于表示副作用。
EECOLOR

6

如果使用Nothing,则无需执行任何操作(包括打印控制台)。如果使用,请使用输出类型Unit

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

...那怎么用Nothing

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

1
此外,EOption必须在协变的位置是:trait Option[+E]允许之类的东西val x: Option[Int] = None
VIM

5

我从未真正使用过该Null类型,但是您使用Unit了Java上的类型voidNothing是一种特殊类型,因为正如Nathan已经提到的那样,不可能有的实例NothingNothing是所谓的底部类型,这意味着它是任何其他类型的子类型。这就是(和相反类型参数)为什么可以在Nil-之前加上任何值的原因-它是List[Nothing]-并且列表将是此元素类型。None也是类型的Option[Nothing]。每次尝试访问此类容器内的值都会引发异常,因为这是从type方法返回的唯一有效方法Nothing


谢谢,我不知道None不会扩展Option [Nothing]。这在某些泛型用法中是有意义的,其中子类型可以不使用任何东西(我想很难找到Null和Unit的例子...)
Sebastien Lorber

在发生副作用的地方使用单位,例如IO monad的类型IO[Unit]可以打印到控制台等。
drexin

是的,从未使用过IO monad,将它与Unit一起使用是有意义的(如果是某些IO操作产生无限流,也许就没有了?)
Sebastien Lorber

不,那里没有任何意义。
drexin

3

没有什么经常被隐式使用。在下面的代码, val b: Boolean = if (1 > 2) false else throw new RuntimeException("error") 所述其他子句是类型没有什么,这是布尔的子类(以及任何其他AnyVal)。因此,尽管else子句实际上并未返回任何内容,但整个分配对编译器均有效。


1

这是Nothing来自的示例scala.predef

  def ??? : Nothing = throw new NotImplementedError

如果您不熟悉(搜索引擎无法搜索它)???,Scala的占位符功能可用于尚未实现的任何内容。就像科特林的一样TODO

创建模拟对象时,可以使用相同的技巧:使用自定义notUsed方法覆盖未使用的方法。不使用的好处???是,您不会为从未打算实现的事情得到编译警告。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.