Возврат значения из try-catch

Вот метод в 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()
        }
}

Он жалуется на

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

Если я добавил дополнительное значение, чтобы:

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
}

он сказал бы

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

который является нечетным - это не то же самое, что и первый подход?

В любом случае, какой стандартный способ справиться с такими ситуациями Scala - вернуть значение из try { .. } catch { .. }?

Ответ 1

Проблема заключается в том, что в случае исключения нет значения для возврата. Поэтому вам нужно решить, какую ценность вернуть в таком случае. Это может быть пустой список (если вы действительно не заботитесь об обработке ошибки - не рекомендуется!):

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; }
  }

Стандартным способом Scala было бы вернуть Option, что дает понять вызывающему, что произошло:

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; }
  }

или, возможно, вернуть само исключение:

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)
  }

Поскольку этот шаблон относительно распространен, для этой цели существует специальный Scala класс Try, который дает эти понятия осмысленным имена и добавляет много полезных методов. Try[A] инкапсулирует результат вычисления, который возвращает A, или исключение, если сбой вычисления:

 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]

поэтому литеральный переписать ваш метод будет

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)
  }

Но, конечно, Scala имеет методы для этого шаблона (после всего, что для Try), поэтому вы можете написать просто

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

(Существует небольшая разница, Try { ... } также ловит некоторые Error s).

Ответ 2

Он жалуется, что не все ветки возвращают результат:

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
        }
}

Общим типом чего-то и единицы является любой. Итак, что вам нужно делать в обоих случаях (с переменной или без переменной результата), нужно вернуть значение в обеих ветвях (возможно, какое-то фиктивное значение, например, пустой список в случае catch).

ИЗМЕНИТЬ

Ошибки различны, потому что без компилятора val можно отслеживать этот поток до ветки и заметить, что тип возвращаемой функции и результат catch различны. С val не было ограничений типа на val, поэтому он может с радостью заключить, что val result имеет любой тип, а затем, когда вы возвращаете результат, он сталкивается с типом результата функции. Если вы указываете тип результата явно, как val result: List[String] = ... сообщение об ошибке будет таким же.

Ответ 3

В Scala такие вещи лучше делать с монадическим стилем:

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

Обновление

Если вы посмотрите на реализацию Try apply, вы увидите и интересный код:

/** 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)
  }

поэтому Try - это просто оболочка для try/catch для монадической цепочки

Ответ 4

Вы можете передать "null" в качестве типа возврата для большинства объектов и типов строк (которые являются подтипами AnyRef). Например, см. Ниже

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}

}