Возврат значения из 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}
}