20

Here is a method in Scala:

def method1(arg: String*): List[String] = {
        try {
          new MyClass(new URL(arg(0)))
            .map(x => x.getRawString.toString)
        }
        catch {
          case e: Exception => e.printStackTrace()
        }
}

It complains on

found   : Unit
[error]  required: List[String]

If I added an additional value so that:

def method1(arg: String*): List[String] = {
        val result = try {
          new MyClass(new URL(arg(0)))
            .map(x => x.getRawString.toString)
        }
        catch {
          case e: Exception => e.printStackTrace()
        }

       result
}

it would say

found   : Any
[error]  required: List[String]

which is odd -- isn't this the same as the first approach?

Anyway, what is the Scala's standard way of dealing with this kind of situations -- returning a value from try { .. } catch { .. } ?

4

4 回答 4

35

The problem is that in the case of an exception, there is no value to return. So you have to decide what value to return in such a case. It can be an empty list (if you really don't care about handling the error - not recommended!):

def method1(arg: String*): List[String] =
  try {
    new MyClass(new URL(arg(0)))
        .map(x => x.getRawString.toString)
  } catch {
    case e: Exception => { e.printStackTrace(); Nil; }
  }

A standard Scala way would be to return an Option, which makes it clear to the caller what happened:

def method1(arg: String*): Option[List[String]] =
  try {
    Some(new MyClass(new URL(arg(0)))
        .map(x => x.getRawString.toString))
  } catch {
    case e: Exception => { e.printStackTrace(); None; }
  }

or perhaps return the exception itself:

def method1(arg: String*): Either[Exception,List[String]] =
  try {
    Right(new MyClass(new URL(arg(0)))
        .map(x => x.getRawString.toString))
  } catch {
    case e: Exception => Left(e)
  }

Since this pattern is relativelly common, there is a special Scala class Try just for this purpose, that gives these concepts meaningful names and adds many helpful methods. Try[A] encapsulates the result of a computation that returns A, or an exception if the computation failed:

 sealed abstract class Try[+T]
 final case class Success[+T](value: T) extends Try[T]
 final case class Failure[+T](exception: Throwable) extends Try[T]

so a literal rewrite of your method would be

def method1(arg: String*): Try[List[String]] =
  try {
    Success(new MyClass(new URL(arg(0)))
        .map(x => x.getRawString.toString))
  } catch {
    case e: Exception => Failure(e)
  }

But of course Scala has methods for this pattern (after all that's what Try is for), so you can write just

def method1(arg: String*): Try[List[String]] =
  Try { new MyClass(new URL(arg(0)))
        .map(x => x.getRawString.toString)) }

(There is a slight difference, Try { ... } also catches some Errors).

于 2013-07-06T09:16:43.553 回答
6

It complains because not all branches return the result:

def method1(arg: String*): List[String] = {
        try {
          new MyClass(new URL(arg(0)))
            .map(x => x.getRawString.toString) // ok, here I know what to do
        }
        catch {
          case e: Exception => e.printStackTrace() // ???? What to return here??? 
                                                   // ok, will return Unit
        }
}

The common type of Something and Unit is Any. So what you need to do in both cases (with, or without result variable) is to return value in both branches (possibly some dummy value, like empty List in catch case).

EDIT

The errors are different because without val compiler can track that flow down to the branch and notice that function return type and catch result are different. With val there was no type constraints on val so it can happily infer that val result has Any type and then, when you return result it confronts with function result type. If you specify result type explicitly as val result: List[String] = ... error message will be the same.

于 2013-07-06T07:59:44.090 回答
5

In Scala such things should be better done with monadic style:

def fun(arg: Strign*): Option[List[String]] = Try {
    new MyClass(new URL(arg(0))).map(_.getRawString.toString)
}.toOption

Update

If you look at the implementation of Try's apply you'll see and interesting code:

/** Constructs a `Try` using the by-name parameter.  This
 * method will ensure any non-fatal exception is caught and a
 * `Failure` object is returned.
 */
def apply[T](r: => T): Try[T] =
  try Success(r) catch {
    case NonFatal(e) => Failure(e)
  }

so Try is just a wrapper for try/catch for monadic chaining

于 2013-07-06T08:17:04.857 回答
1

You could pass "null" as return type for most of the objects and String Types(which are subtypes of AnyRef). For example please see below

def execute_rs(sql:String): ResultSet = {



try {
  println("SQL IS " + sql)

  val con = DriverManager.getConnection(url, username, password)
  // println("statemetn created1")
  val stmt = con.createStatement()
  //println("statemetn created")
  //str.foreach( i =>statement.addBatch(i))
  val rs = stmt.executeQuery(sql)

  return rs
}
catch  {
  case e: SQLException=> {println("exception occured in oracle sql insertion"+e); null}

  case e: Exception=> {println("Common exception occured:"+e);null}
  case _:Throwable => {println("some other exception occured");null}

}
于 2017-04-30T02:48:34.640 回答