Skip to content

Instantly share code, notes, and snippets.

@yongjhih
Created August 17, 2023 15:14
Show Gist options
  • Save yongjhih/51e0c1bb60d6e57300a16dd88880c451 to your computer and use it in GitHub Desktop.
Save yongjhih/51e0c1bb60d6e57300a16dd88880c451 to your computer and use it in GitHub Desktop.
fun <T : Any> Class<T>.getDeclaredFieldOrNull(name: String): Field? =
tryOrNull { getDeclaredField(name) }
@Suppress("SpreadOperator")
fun <T : Any> Class<T>.getDeclaredMethodOrNull(name: String, vararg parameterTypes: Class<*>): Method? =
tryOrNull { getDeclaredMethod(name, *parameterTypes) }
@Suppress("SpreadOperator")
fun <T : Any> Class<T>.getDeclaredConstructorOrNull(vararg parameterTypes: Class<*>): Constructor<T>? =
tryOrNull { getDeclaredConstructor(*parameterTypes) }
fun <V: Any?> Any.setField(name: String, value: V): Field? = tryOrNull {
[email protected](name, value)
}
fun <V: Any?> Any.setFieldOrThrow(name: String, value: V): Field = javaClass.getDeclaredField(name).apply {
isAccessible = true
set(this@setFieldOrThrow, value)
}
class TypedValues {
val list: MutableList<Pair<Class<*>, Any?>> = mutableListOf()
@JvmName("addPair")
fun <T: Any> add(pair: Pair<Class<T>, T?>) = list.add(pair)
@JvmName("addNullablePrimitive")
inline fun <reified T: Any> add(value: T?) = add((T::class.javaPrimitiveType ?: T::class.java) to value)
}
/**
* Usage:
*
* ```
* val result = obj.invokeMethod<String>("methodName") {
* ```
*/
@Suppress("SpreadOperator")
fun <V: Any?> Any.invokeMethodOrThrow(name: String, onParameters: TypedValues.() -> Unit = {}): V = run {
val (types, values) = TypedValues().apply(onParameters).list.unzip()
javaClass.getDeclaredMethod(name, *(types.toTypedArray())).run {
@Suppress("UNCHECKED_CAST")
invoke(this@invokeMethodOrThrow, *(values.toTypedArray())) as V
}
}
fun <V: Any?> Any.invokeMethod(name: String, onParameters: TypedValues.() -> Unit = {}): V? = tryOrNull {
[email protected](name, onParameters)
}
@Suppress("SpreadOperator")
inline fun <reified T: Any> Class<T>.newInstanceOrThrow(onParameters: TypedValues.() -> Unit = {}): T = run {
val (types, values) = TypedValues().apply(onParameters).list.unzip()
getDeclaredConstructor(*(types.toTypedArray())).run { newInstance(*(values.toTypedArray())) }
}
inline fun <reified T: Any> Class<T>.newInstance(onParameters: TypedValues.() -> Unit = {}) = tryOrNull {
[email protected](onParameters)
}
@yongjhih
Copy link
Author

    @Test
    fun setField() {
        val range = IntProgression.fromClosedRange(0, 10, 1)
        range.setField("first", 2)
        range.first shouldBe 2
    }

    @Test
    fun invokeMethod() {
        IntProgression.fromClosedRange(0, 10, 1).invokeMethod<Boolean>("isEmpty") shouldBe false
        IntProgression.fromClosedRange(0, -1, 1).invokeMethod<Boolean>("isEmpty") shouldBe true
    }

    @Test
    fun newInstance() {
        IntRange::class.java.newInstance {
            add(Int::class.java to 0)
            add(Int::class.java to 1)
        }?.isEmpty() shouldBe false
        IntRange::class.java.newInstance {
            add(Int::class.java to 0)
            add(Int::class.java to -1)
        }?.isEmpty() shouldBe true
        IntRange::class.java.newInstance {
            add(Int::class.java to 0)
            add(Int::class.java to -1)
            add(Int::class.java to 2)
        } shouldBe null
        IntRange::class.java.newInstance {
            add(0)
            add(-1)
        }?.isEmpty() shouldBe true
        IntRange::class.java.newInstance {
            add<String>(null)
            add<String>(null)
        } shouldBe null
        IntRange::class.java.newInstance {
            add<Int>(null)
            add<Int>(null)
        } shouldBe null
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment