scala中条件返回的正确习惯用法

时间:2013-12-05 11:36:51

标签: scala

我试图找出scala方式来实现我在java中一直做的事情。 在java中我会让snarf_image(下面)在满足if条件时返回null,否则返回bArray。 scala的方法是什么?这段代码甚至无法编译,我无法找到正确的方法 - 我确信我的想法已经完成。

 def snarf_image ( sUrl : String ) : Array[Byte] = {
   val bis = new BufferedInputStream(new URL(sUrl.replace(" ", "%20")).openStream())
   val bArray = Stream.continually(bis.read).takeWhile(-1 !=).map(_.toByte).toArray
   val img = ImageProcessing.ArrayToImage(bArray)
   if ( img.getHeight < 100 || img.getWidth < 100 ) {
     Empty
   } else {
     bArray
   }
 }

对于记录我使用电梯(因此使用空)但我很确定这更像是一个scala问题。

2 个答案:

答案 0 :(得分:8)

有时候,如果要返回null(在Java中),可以使用Option

我没有编译它,但它应该可以工作。

def snarf_image ( sUrl : String ) : Option[Array[Byte]] = {
   val bis = new BufferedInputStream(new URL(sUrl.replace(" ", "%20")).openStream())
   val bArray = Stream.continually(bis.read).takeWhile(-1 !=).map(_.toByte).toArray
   val img = ImageProcessing.ArrayToImage(bArray)
   if ( img.getHeight < 100 || img.getWidth < 100 ) {
     None
   } else {
     Some(bArray)
   }
}

答案 1 :(得分:4)

TLDR:有很多选项可供使用:Option,Box,Either,Try甚至Future(还有更多可以在不同的库中找到),可能你会对Option有好处

首先,您可以使用空集合而不是null - 它不仅适用于Scala,而且适用于许多其他语言,包括Java和C#:

def snarf_image ( sUrl : String ) : Array[Byte] = {
   // ...
   if ( img.getHeight < 100 || img.getWidth < 100 ) {
     Array.empty[Byte]
   } else {
     bArray
   }
 }

当然,这可能无法让您远离惊喜,因为您可能会意外忘记检查收集是否空虚,但您更有可能会因为null而获得巨大的惊喜。

另见Is it better to return null or empty collection?

接下来是Option。

Option

当你有某些东西(Some)或没有东西(None)时使用选项,而你不关心原因(只有一个原因或无关紧要)。

该选项不是scala发明(我已经看过ML语言,在Haskell中它被称为Maybe,它甚至是comes to the std lib in java 8available to use in earlier java versions as a guava part)。

它在标准库中广泛使用,您可能会在许多第三方库中看到它。典型的例子可以是从Map中检索,当没有这样的密钥时 - 选项强调Map不能包含这样的密钥,所以你必须要么 - 处理丢失密钥的可能性或进一步传播它。

val xys = Map(1 -> "foo", 2 -> "bar")
xys.get(3)
// Option[String] = None
xys.get(1)
// Option[String] = Some(foo)
xys.get(2).toUpperCase
// cannot operate directly on Option -- has to unwrap it first:
// error: value toUpperCase is not a member of Option[String]

Either

当您想知道为什么 时,当您有两种可能性(不仅是成功/失败)时,请使用使用旧版本的scala(2.10之前)

它与Option完全相同,不仅用于Scala,还用于其他一些语言(例如已经提到过的Haskell)。除了可疑的区别 - 要么不是Scala中的monad,主要区别在于你没有 empty 元素,你有 right left < / em>的。这是一种比Option不太常用的方式,因为它不能立即清楚什么应该是正确的,应该留下什么,但是the common convention is to use Right for the successful value and Left for a faulty path(通常它包含Throwable对象,并解释出了什么问题)。 Either主要是由Try发出的,这不是fabulos和originally born in Twitter(最大和最早的Scala采用者之一)。

Try

在scala 2.10+中使用try成功的值/失败原因(或者2.9.3,因为它被反向移植)

从名为“成功与失败”的案例开始,它更方便一些。

来自official doc的例子(顺便说一句,这很好):

导入scala.util。{尝试,成功,失败}

def divide: Try[Int] = {
  val dividend = Try(Console.readLine("Enter an Int that you'd like to divide:\n").toInt)
  val divisor = Try(Console.readLine("Enter an Int that you'd like to divide by:\n").toInt)
  val problem = dividend.flatMap(x => divisor.map(y => x/y))
  problem match {
    case Success(v) =>
      println("Result of " + dividend.get + "/"+ divisor.get +" is: " + v)
      Success(v)
    case Failure(e) =>
      println("You must've divided by zero or entered something that's not an Int. Try again!")
      println("Info from the exception: " + e.getMessage)
      divide
  }
}

Future

当你的选项在时空连续体上被分割时使用未来:现在它是空的,但是几秒钟之前它已经完成(但反之亦然 - 未来一个实现过程)。

未来在Scala中是新的,就像Try一样,就像试试它从Twitter的路径一样。它在并发代码中使用,您希望将某些工作发送到后台,并在稍后等待结果或在完成时调用回调。尝试作为一个明确的结果 - 未来成功完成或异常结束(被捕异常)。

请注意,Future(Scalaz,Unfiltered,Twitter,Akka)有很多实现 - 它们可能会与scala.actors.Future统一起来

进一步阅读brilliant overview of scala Futures/Promises

第三方

  • Box应该用于Lift生态系统,基本上是类固醇的选项,作为空/全路径。

  • Or and Every。或模仿Either和Box,Every用于收集许多错误。

  • Validation。在Scalaz库中使用。