Joffrey
06/24/2022, 1:23 PMis
check on Kotlin/JVM, in the sense that it doesn't fail with NoClassDefFoundError
if the class is not on the classpath?
I would like to do something with an object if it's of a given type, with the smart cast, but I don't want it to fail if running on lower JDK that doesn't have that class, I just want the condition to be false, and the code to move on without trying to load the class.Joffrey
06/24/2022, 1:24 PMClass.forName
and unsafe casts, but I wish there was something baked inJoffrey
06/24/2022, 1:25 PMwhen
that handles different types of exceptions. One of them only exists on JDK11+ and the when
is now failing on Android target because of the missing classKlitos Kyriacou
06/24/2022, 1:58 PMcatch (e: Exception) {
when {
ClassLoader.getSystemResource("java/lang/whatever/ThisParticularException.class") != null
&& e is ThisParticularException -> TODO()
}
}
Joffrey
06/24/2022, 2:19 PMClass.forName()
with try/catch insteadJoffrey
06/24/2022, 2:28 PM@OptIn(ExperimentalContracts::class)
private inline fun <reified C : Any> Any.safeIs(className: String): Boolean {
contract {
returns(true) implies(this@safeIs is C)
}
if (!classExists(className)) {
return false
}
checkMatches<C>(className) // prevent developer mistakes
return this is C
}
private inline fun <reified T : Any> checkMatches(className: String) {
val typeName = T::class.qualifiedName
require(typeName == className) {
"Mismatch between the given class name '$className' and the actual parameter type of the action lambda '$typeName"
}
}
private fun classExists(className: String): Boolean = try {
Class.forName(className)
true
} catch (e: ClassNotFoundException) {
false
}
So I can have nice usages like:
if (obj.safeIs<HttpClient>("java.net.http.HttpClient")) {
// obj is smart-cast to HttpClient here
}
Which also includes a check that the fqn and the type param actually matchephemient
06/25/2022, 1:09 AMHttpClient::class.name
to be inlined as a compile-time constantephemient
06/25/2022, 1:37 AMinternal interface Foo {
fun isHttpClient(value: Any): Boolean
}
internal class Java11Foo {
fun isHttpClient(value: Any): Boolean = value is HttpClient
}
fun isHttpClient(value: Any): Boolean {
val iterator = ServiceLoader.load(Foo::class.java).iterator()
while (iterator.hasNext()) {
try {
return iterator.next().isHttpClient(value)
} catch (_: NoClassDefFoundError) {
// continue
}
}
return false
}
// META-INF/services/Foo
Java11Foo