scala Try ... catch ... 最后返回值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18685573/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-22 05:38:40  来源:igfitidea点击:

Try ... catch ... finally return value

scala

提问by Alan Coromano

I found that it's a shame that I can't return a return value from a such simple construction as try ... catch ... finally

我发现我不能从这样简单的构造中返回返回值是一种耻辱 try ... catch ... finally

 def foo: String = {
    val in = new BufferedReader(.....)
    try {
      // val in = new BufferedReader(.....) -- doesn't matter
      in.readLine
    }
    catch {
      case e: IOException => e.printStackTrace()
    }
    finally {
      in.close()
    }
  }

This code doesn't compile. Is there any way to make compile expectusing any libraries, high-level constructions, etc? I want to do that only by using the capacity of pure Scala as a programming language.

此代码无法编译。有什么方法可以使用任何库、高级结构等来使编译期望值?我只想通过使用纯 Scala 作为编程语言的能力来做到这一点。

回答by ToxicFrog

In a scala try-catch-finally block, the finallyblock is evaluated onlyfor side effects; the value of the block as a whole is the value of the last expression in the try(if no exception was thrown) or catch(if one was).

在 scala try-catch-finally 块中,该finally针对副作用进行评估;块作为一个整体的值是try(如果没有抛出异常)或catch(如果有)中最后一个表达式的值。

If you look at the output from the compiler, you'll note that it's complaining about the contents of the catchblock, not the finally:

如果您查看编译器的输出,您会注意到它在抱怨catch块的内容,而不是finally

$ scala test.scala
/tmp/test.scala:12: error: type mismatch;
 found   : Unit
 required: String
    case e: Exception => e.printStackTrace()

This is because Exception.printStackTrace()returns Unit, so the return type of the function would have to be Stringif the trysucceeded and Unitotherwise.

这是因为Exception.printStackTrace()回报Unit,因此函数的返回类型必须是String如果try成功,Unit否则。

You can address this by having the catchblock evaluate to a String as well:

您也可以通过让catch块评估为字符串来解决这个问题:

catch {
  case e: IOException => {
    e.printStackTrace()
    e.toString()
  }
}

Of course, this means there has to be some useful string value you can return even when an error occurs (perhaps ""?); a more idiomatic approach might be to return an Option[String], with the tryblock returning Some(in.readLine)and the catchblock returning None. In either case, though, the value of both the tryand catchblocks must match the function signature. The finallyblock's type is irrelevant.

当然,这意味着必须有一些有用的字符串值即使发生错误也可以返回(也许是""?);更惯用的方法可能是返回 an Option[String]try块返回Some(in.readLine)catch块返回None。但是,无论哪种情况,块trycatch块的值都必须与函数签名匹配。该finally模块的类型无关。

For reference, here's a version that passes type checking and works:

作为参考,这是一个通过类型检查并有效的版本:

import java.io.BufferedReader
import java.io.InputStreamReader
import java.io.IOException

def foo: String = {
  val in = new BufferedReader(new InputStreamReader(System.in))
  try {
    in.readLine
  }
  catch {
    case e: IOException => { e.printStackTrace(); e.toString() }
  }
  finally {
    in.close()
  }
}

System.out.println("Return value: " + foo)

in.close()returns Unit, but that's ok because the value of the finallyblock is ignored. The tryand catchblocks both return String.

in.close()返回 Unit,但这没关系,因为finally块的值被忽略了。在trycatch块都返回字符串。

回答by Alex

I think it would help to start with the Java conception of an exception. A Java method is basically a contract to do something (return a value or cause a side effect) if it is called. The method makes certain assumptions (for example, that the operating system will cooperate with a request to read a file). Sometimes, if these conditions are not met, it will return a null value, and sometimes it will stop execution completely and "throw an exception".

我认为从 Java 的异常概念开始会有所帮助。Java 方法基本上是在调用时执行某事(返回值或引起副作用)的契约。该方法做出某些假设(例如,操作系统将配合读取文件的请求)。有时,如果不满足这些条件,它会返回一个空值,有时它会完全停止执行并“抛出异常”。

This can be a problem, because the contract of a Java method is not always clear. A Java method that declares a return type of String really has three outcomes: a String value, null, or an exception. One problem with exceptions is that they halt execution of the code, possibly masking other problems further down in the method or possibly failing to close resources that were opened (which is the reason for try, catch, finally)

这可能是一个问题,因为 Java 方法的约定并不总是很清楚。声明 String 返回类型的 Java 方法实际上具有三种结果:String 值、null 或异常。异常的一个问题是它们会停止代码的执行,可能会在方法中进一步掩盖其他问题,或者可能无法关闭已打开的资源(这是 的原因try, catch, finally

Scala seeks clarity regarding the return type. One way to do this would be to collect all the exceptions that occur in the method and then pass that list of exceptions as a return value. But then, we need to have a return type that says "We're going to return something, or we might return nothing" (scala.Option), or perhaps, "We're going to return Either the expected answer or we'll return information about why the expected answer is not being returned" (scala.util.Either), or perhaps, "We're going to Try to do a risky operation, which might result in Success or Failure." (scala.util.Try)

Scala 寻求关于返回类型的清晰性。一种方法是收集方法中发生的所有异常,然后将该异常列表作为返回值传递。但是,我们需要有一个返回类型,表示“我们将返回一些东西,或者我们可能什么都不返回”(scala.Option),或者,“我们将返回预期的答案或我们”将返回有关为什么未返回预期答案的信息”(scala.util.Either),或者,“我们将尝试进行有风险的操作,这可能会导致成功或失败。” ( scala.util.Try)

Scala deals with the possibility of a null value with Option. Option is class that has two subclasses: Noneand Some, which is a container that holds exactly one element. For example:

Scala 使用 Option 处理空值的可能性。Option 是具有两个子类的类:Noneand Some,它是一个只包含一个元素的容器。例如:

val contacts = Map("mark" -> 1235551212, "john" -> 2345551212, "william" -> 3455551212)
val phoneOfJohn: Option[Int] = contacts.get("john")
val phoneOfAlex: Option[Int] = contacts.get("alex")

phoneOfJohn match {
  case Some(number) => callPhone(number) 
  case None         => Logger.debug("unable to call John, can't find him in contacts")
}

phoneOfAlex match {
  case Some(number) => callPhone(number) 
  case None         => Logger.debug("unable to call Alex, can't find him in contacts")
}

This codes make a phone call to John, and it will log the fact that it was unable call Alex because it couldn't find his phone number in the phone book. But an Optiondoesn't provide information about why no value was returned. If we want to collect those reasons, we can use an Either. The Eitherhas two subclasses: A Leftcould store all of the exceptions that were collected in the process of doing the "risky operation" while a Rightwould be similar to a Someand contain the expected value.

这个代码给 John 打电话,它会记录一个事实,即它无法呼叫 Alex,因为它在电话簿中找不到他的电话号码。但是 anOption不提供有关为什么没有返回值的信息。如果我们想收集这些原因,我们可以使用Either. 将Either有两个子类:一个Left可以存储所有被收集在做“危险操作”的过程是异常的同时Right将类似于一个Some和包含的预期值。

Doing a foldoperation on an Either to convert it to a Left or a Right is somewhat counter-intuitive, and thus we come to scala.util.Try.

对Either执行折叠操作以将其转换为Left 或Right 有点违反直觉,因此我们来到scala.util.Try。

@marius, a scala developer at Twitter, wrote a very good postabout the rationale for adopting scala.util.Try. I think this is what you're looking for.

@marius是 Twitter 的 scala 开发人员,他写了一篇关于采用scala.util.Try的基本原理的非常好的帖子。我想这就是你要找的。

The essence of scala.util.Try is that a risky action can result in Successor Failure. Before scala.util.Try, developers would use an Option or Either. Here's what it would look like if you did a buffered reader from a file:

scala.util.Try 的本质是风险操作可能导致SuccessFailure。在 scala.util.Try 之前,开发人员会使用 Option 或任一。如果你从一个文件做一个缓冲读取器,它会是什么样子:

import scala.util.{Try, Failure, Success}

def foo(fileName: String): Try[String] = Try {
  scala.io.Source.fromFile(fileName).bufferedReader().readLine()
}

def bar(file: String): Unit = foo(file) match {
  case Success(answer) => Logger.info(s"$file says the answer is $answer")
  case Failure(e) => Logger.error(s"couldn't get answer, errors: ${e.getStackTrace}")
}

bar("answer.txt") \ will log the answer or a stack trace

Hope this helps!

希望这可以帮助!

回答by Jatin

This is happening because in Scala unlike Java, try-catch are expressions. In your code, try block returns a Stringand your catch block return type is Unit(as you are just printing and returning nothing).

这是因为在 Scala 中与 Java 不同,try-catch 是表达式。在您的代码中, try 块返回 aString并且您的 catch 块返回类型是Unit(因为您只是打印并且不返回任何内容)。

The type-inference hence takes the return type Tsuch that T >: Unitand T >: String. Hence Tis of type Any. And because you have declared foo as def foo: String. The compiler throws an error as it was expecting String but found Any.

因此,类型推断需要的返回类型T,使得T >: UnitT >: String。因此T是 类型Any。并且因为您已将 foo 声明为def foo: String. 编译器抛出错误,因为它期望 String 但找到 Any。

What you can do is to return a default value under you catch block:

您可以做的是在 catch 块下返回一个默认值:

try{
  in.readLine
}
catch {
  case e: IOException => {
             e.printStackTrace()
             "error string"
         }
}
 finally{
     in.close
 }

回答by Vlad Patryshev

This method returns a Unit; you have to return something in the catch block; or I'd rather suggest to change the return type to e.g. Option; or use Tryclass.

此方法返回一个Unit; 你必须在 catch 块中返回一些东西;或者我宁愿建议将返回类型更改为 eg Option; 或使用Try类。

What is obvious - you don't always have a string in this situation. In Java people tend to ignore the realities, but this is Scala, one can do better.

很明显 - 在这种情况下你并不总是有一个字符串。在 Java 中人们往往会忽视现实,但这是 Scala,可以做得更好。