¿Por qué Kotlin recibe una exception detectable no declarada en lugar de una exception ParseException?

Tengo un método de extension que convierte cadenas en Date en Kotlin.

 fun String.convertToDate() : Date { var pattern: String = "dd-mm-yyyy" val dateFormatter = SimpleDateFormat(pattern) return dateFormatter.parse(this) // parse method throw ParseException } 

Y este es el código en el que trato de detectar posibles excepciones.

  try { "22---2017".convertToDate() } catch (ex: ParseException) { // ParseException supposed to be caught in this block logger.error("Parse exception occur") } catch (ex: Exception) { // ParseException caught in this block logger.error("Exception occur") } 

La ParseException capturada en el último bloque que es donde se ParseException Exception . Pero, ¿debería atraparse en el bloque ParseException? Que me estoy perdiendo aqui ?

=== ACTUALIZACIÓN ===

Estoy desarrollando un proyecto Spring MVC. He ejecutado el código en el sencillo progtwig kotlin independiente donde se comporta de manera acorde. Pero en mi proyecto de spring se comporta de manera diferente. Le doy el código completo, el Controller y la capa de Service .

Controlador

 @PostMapping @PreAuthorize("hasAnyRole('USER','ROLE_USER','ROLE_ADMIN','ADMIN')") fun postAttendance(@RequestBody attendanceJson: AttendanceJsonWrapper, request: HttpServletRequest): ResponseEntity<*> { val organization = getOrganizationFromSession(request) try { val attendanceBook: AttendanceBook = attendanceService.post(attendanceJson, organization.id!!) logger.info("Post successfully attendance book {}", attendanceBook) } catch (ex: SameDateAttendanceException) { logger.error("Duplicate attendance entry found at date [{}]", attendanceJson.date, attendanceJson.classId) return responseConflict(attendanceJson) } catch (ex: java.text.ParseException) { ex.printStackTrace() logger.error("Parse exception occur") return responseError(ErrorObject( attendanceJson, "date", Constants.INVALID_DATE_FORMAT, Constants.EXPECTED_DATE_FORMAT)) } catch (ex: Exception) { ex.printStackTrace() logger.error("Exception occur") if (ex.cause is ParseException) { logger.info("What the hell is happening") } return responseOK(attendanceJson) } 

Servicio

 @Service open class AttendanceService constructor(val attendanceRepository: AttendanceRepository) { @Transactional open fun post(attendanceJsonWrapper: AttendanceJsonWrapper, orgId: Long): AttendanceBook { // ParseException should thrown from this line. val _attendanceDate = attendanceJsonWrapper.date.convertToDate() // Other logic goes here return attendanceRepository.save(attendanceBook) } } 

Iniciar session

para la input dada 28--2017 del front-end después del logging producido.

 2017-06-28 02:36:52.942 ERROR 4632 --- [nio-8080-exec-1] clcrest.AttendanceRestController : Exception occur 2017-06-28 02:52:32.485 INFO 2796 --- [io-8080-exec-10] clcrest.AttendanceRestController : What the hell is happening 

Excepción

 java.lang.reflect.UndeclanetworkingThrowableException at com.lynas.service.AttendanceService$$EnhancerBySpringCGLIB$$7b42c004.post(<generated>) at com.lynas.controller.rest.AttendanceRestController.postAttendance(AttendanceRestController.kt:34) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205) at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:133) at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:97) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:827) at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:738) at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:85) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:963) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970) at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872) at javax.servlet.http.HttpServlet.service(HttpServlet.java:661) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846) at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.boot.web.filter.ApplicationContextHeaderFilter.doFilterInternal(ApplicationContextHeaderFilter.java:55) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.boot.actuate.trace.WebRequestTraceFilter.doFilterInternal(WebRequestTraceFilter.java:110) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:317) at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:127) at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:91) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:114) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:137) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:111) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:170) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) at org.springframework.security.web.csrf.CsrfFilter.doFilterInternal(CsrfFilter.java:124) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:64) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:56) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:214) at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:177) at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346) at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:262) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:105) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.springframework.boot.actuate.autoconfigure.MetricsFilter.doFilterInternal(MetricsFilter.java:106) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:478) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:80) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:799) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1455) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) at java.lang.Thread.run(Thread.java:745) Caused by: java.text.ParseException: Unparseable date: "28--2017" at java.text.DateFormat.parse(DateFormat.java:366) at com.lynas.util.UtilKt.convertToDate(Util.kt:56) at com.lynas.service.AttendanceService.post(AttendanceService.kt:23) at com.lynas.service.AttendanceService$$FastClassBySpringCGLIB$$2b941a4b.invoke(<generated>) at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:738) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157) at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99) at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:282) at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:673) ... 103 more 

    The UndeclanetworkingThrowableException es causada por kotlin. ¿por qué? sabemos que kotlin no tiene excepciones marcadas.

    La documentation de UndeclanetworkingThrowableException dice:

    Lanzado por una invocación de método en una instancia de proxy si su método de invocación de manejador de invocación arroja una exception marcada

    Por otro lado, Kotlin puede lanzar cualquier exception, pero en java, serán: exception y error no comprobados. sabemos que casi todos los frameworks populares se crean java.reflect package java.reflect incluye java.reflect.Proxy .

    En resumen, cuando la function kotlin lanza una exception marcada java y no declara la exception que tal vez arrojará. luego, llame a un Proxy java para que reciba tal Excepción acumulable no declarada .

    En java puedes declarar que se lanzará una exception comprobada de la siguiente manera:

     // v--- it is a checked exception in java int read() throws IOException{/**/} 

    Gracias por @ glee8e a los puntos de mi error. También puede lanzar una exception en kotlin, ya que kotlin no tiene throws keyword, por lo que debe usar @Throws para declarar que se @Throws una exception:

     @Throws(IOException::class) fun read():Int{/**/} 

    copiemos la exception UndeclanetworkingThrowableException en kotlin:

     //throws a UndeclanetworkingThrowableException takes the original IOException as its cause // because java.lang.Runnable don't declare any checked exception at all // | // v Runnable::class.proxying(::throwsAJavaCheckedException).run() // throws the original IOException directly because java.util.concurrent.Callable // has declanetworking that it will be throwing a checked Exception // | // v Callable::class.proxying(::throwsAJavaCheckedException).call() 

     fun throwsAJavaCheckedException(proxy:Any, method:Method, args:Array<Any>?): Any? { throw IOException(); } typealias Invocation = (Any, Method, Array<Any>?) -> Any?; fun <T:Any> KClass<T>.proxying(handler:Invocation) = cast(Proxy.newProxyInstance( ClassLoader.getSystemClassLoader(), arrayOf(java), handler )); 

    ¿Cómo evitar este problema?

    Si la function la escribe usted mismo, la solución es más simple. sí, declara que la function arrojará una exception marcada. por ejemplo:

     @Throws(ParseException::class) fun convertToDate(){/**/} 

    O escriba algún plugin de allopen como allopen , lo allthrows aquí.

    Pero también puedes hacer algunos compromisos. Si no está seguro de lo que sucederá en los frameworks como spring, debe envolver su invocación en un método de ayuda. por ejemplo:

     val task = Runnable::class.proxying(::throwsAJavaCheckedException) // v-- the result return by catch-block immediately if no exception occurs val result : Unit = catch(task::run).by { actual: Throwable -> val exceptional: Unit = Unit; // v--- you can choose return an exceptional value or rethrow the exception when (actual) { is RuntimeException -> exceptional is ParseException -> logger.info(acutal) else -> throw actual } } val result : Unit? = catch(task::run).only { actual:Throwable -> // only handle the exception don't return the exceptional value logger.info(actual); } 

     inline fun <T> catch(crossinline block: () -> T): () -> T { return { block(); }; } inline fun <T> (() -> T).by(exceptionally: (Throwable) -> T): T { return only { exceptionally(it) }!! } inline fun <T : R, R> (() -> T).only(exceptionally: (Throwable) -> R): R? { try { return invoke(); } catch(e: UndeclanetworkingThrowableException) { return exceptionally(e.cause ?: e); } catch(e: Exception) { return exceptionally(e); } } 

    Parece que su service se ejecuta en un context de invocación diferente al de su controller. Como el service arroja la exception, no puede atraparlo en el controller; parece que está llamando al service directamente, pero debido a la inyección de código realmente no lo es. Entonces, lo que sucede es que el context de invocación (generalmente un hilo) termina con una exception. Esto se traduce en una exception de UndeclanetworkingThrowableException con la exception original como causa.

    Hay dos forms de lidiar con esto:

    1. capte la exception localmente en el service donde se genera la exception;
    2. atrapa la UndeclanetworkingThrowableException en un try / catch separado y luego vuelve a lanzar la causa.

    La primera opción debe ser preferida pero requiere que maneje la exception en el service. El segundo se parece demasiado a un truco para mí, pero no requiere configurar el event handling excepciones en el service en lugar del controller.