Why Kotlin receives such an UndeclaredThrowableException rather than a ParseException?

前端 未结 2 1501
生来不讨喜
生来不讨喜 2021-02-19 06:32

I have an extension method that converts string to Date in Kotlin.

fun String.convertToDate() : Date {
  var pattern: String = \"dd-mm-yyyy\"
  val         


        
2条回答
  •  说谎
    说谎 (楼主)
    2021-02-19 07:11

    The UndeclaredThrowableException is caused by kotlin. why? we know kotlin does not have checked exceptions.

    The documentation of UndeclaredThrowableException says:

    Thrown by a method invocation on a proxy instance if its invocation handler's invoke method throws a checked exception

    On the other hand, Kotlin can throw any exceptions but in java them will are: unchecked/checked exception and error. we know almost all of the popular frameworks is created base on java.reflect package includes java.reflect.Proxy.

    In short, when the kotlin function throw a java checked exception and don't declare the exception that it will maybe throwing. then call a java Proxy you maybe receive such a UndeclaredThrowableException.

    In java you can declare a checked-exception will be throwing as below:

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

    Thanks for @glee8e to points my mistake. you also can throws an exception in kotlin, since kotlin don't have throws keyword so you must using @Throws to declare an exception will be throwing:

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

    let's reproduce the UndeclaredThrowableException in kotlin:

    //throws a UndeclaredThrowableException 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 declared that it will be throwing a checked Exception
    //                                                      |
    //                                                      v
    Callable::class.proxying(::throwsAJavaCheckedException).call()
    

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

    How to avoiding this problem?

    If the function is wrote by yourself the solution is so simpler. yes, declare the function will be throwing a checked exception. for example:

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

    OR write some gradle-plugin like as allopen, I named it allthrows here.

    But you also can make some compromises. If you are not sure what will be happens in the frameworks like as spring, you should wrap your invocation into a helper method. for example:

    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  catch(crossinline block: () -> T): () -> T {
        return { block(); };
    }
    
    inline fun  (() -> T).by(exceptionally: (Throwable) -> T): T {
        return only { exceptionally(it) }!!
    }
    
    inline fun  (() -> T).only(exceptionally: (Throwable) -> R): R? {
        try {
            return invoke();
        } catch(e: UndeclaredThrowableException) {
            return exceptionally(e.cause ?: e);
        } catch(e: Exception) {
            return exceptionally(e);
        }
    }
    

提交回复
热议问题