Scala从2.12.4升级到2.12.18后try-catch编译报错求助
我有一个会在Future中抛出自定义异常的方法,调用方代码如下:
try { Future<Option<type>> futureRet = doWork(input); Option<type> res = Await$.MODULE$.result(futureRet, Duration.apply("30s")); } catch (CustomException e) { return customExceptionHandler(e); } catch (Exception e) { return handleException(request, e); }
可能返回Future.failed()的方法代码如下:
override def doWork( input: String ): Future[Option[ReturnType]] = { if (!input.isEmpty) { completeWork(input) } else { Future.failed(CustomException("bad input")) } }
CustomException的定义如下:
case class CustomException(er: String) extends Exception(s"error $er")
上述代码在Scala 2.12.4中运行正常,但升级到Scala 2.12.18后出现编译错误:"exception is never thrown in the corresponding try block"。有趣的是,即使移除catch (CustomException e)块,当doWork抛出异常时,调试器显示该异常为CustomException类型,但仍会出现编译错误。请问该如何修复?Scala版本间有哪些变化导致了这个问题?
Scala 2.12.10及后续版本对Await.result的编译检查做了严格优化。旧版本中,编译器不会严格校验Future.failed携带的异常类型是否会被Await.result直接抛出;但新版本里,编译器会基于Await.result的方法签名(声明仅抛出InterruptedException和TimeoutException)做静态检查,判定try块内不会抛出CustomException,因此触发编译错误。
实际上,Await.result会把Future内部的异常包装成ExecutionException抛出,调试器中看到的CustomException是ExecutionException的cause,并非直接抛出的顶层异常。旧版本编译器未严格校验这一逻辑,新版本补上了该校验规则。
需要捕获ExecutionException,从中提取内部的CustomException进行处理,修改后的调用方代码示例如下:
try { val futureRet = doWork(input) val res = Await.result(futureRet, Duration.apply("30s")) } catch { case e: ExecutionException if e.getCause.isInstanceOf[CustomException] => val customEx = e.getCause.asInstanceOf[CustomException] customExceptionHandler(customEx) case e: InterruptedException | e: TimeoutException => handleException(request, e) case e: Exception => handleException(request, e) }
也可以用模式匹配直接提取异常cause简化逻辑:
try { val res = Await.result(doWork(input), 30.seconds) } catch { case ExecutionException(customEx: CustomException) => customExceptionHandler(customEx) case otherEx: Exception => handleException(request, otherEx) }
另外注意:原doWork方法中写的Future.failed(customExceptionHandler("bad input"))存在逻辑错误,应该改为Future.failed(CustomException("bad input")),否则是把异常处理后的结果包装成了异常,需要修正。
内容的提问来源于stack exchange,提问作者user888270

