2013-07-06 5 views
10

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

Вы можете получить это на typecheck путем повторного исключения исключения, то есть 'e.printStackTrace(); throw e' или exiting, т. е. 'e.printStackTrace(); sys.exit', в зависимости от того, что вы хотите, - так как выбрасываемые исключения и sys.exit имеют тип 'Nothing', который подклассифицирует все. –

ответ

23

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

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, который дает эти понятия значимым именам и добавляет много hel пылкие методы. 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)) } 

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

+0

, это хороший ответ! –

5

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

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

EDIT

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

+0

Это имеет смысл. Но в чем разница между двумя моими подходами (с использованием и без использования результата val)? Они должны возвращать один и тот же тип, но по какой-то причине ошибки разные. –

+0

@MariusKavansky см. Обновление –

4

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

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

Update

Если посмотреть на реализации Попробуйте Применим вы увидите и интересный код:

/** 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 - это просто обертка для try/catch для монадического цепочки

+1

Вы можете заменить 'toOption'' .map {Some (_)}. Recover {case e: Exception => e.printStackTrace(); None} .get', чтобы получить поведение, как в вопросе. – senia

+0

@senia toOption короче, и я бы не сделал этого таким образом, чтобы избежать бесполезных побочных эффектов – 4lex1v

+0

Как мне это сделать с помощью оператора «попробуйте», а не методом «Попробуйте»? –

0

Вы можете передать «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} 

} 
Смежные вопросы