I have an extension
method that converts string to Date in Kotlin.
fun String.convertToDate() : Date {
var pattern: String = \"dd-mm-yyyy\"
val
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
));
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);
}
}