问题
If I'm on the JVM I can do this:
object Playground {
class DynamicInvocationHandler : InvocationHandler {
@Throws(Throwable::class)
override operator fun invoke(proxy: Any, method: Method, args: Array<Any>): Any {
LOGGER.info("Invoked method: {}", method.name)
return 42
}
companion object {
private val LOGGER = LoggerFactory.getLogger(
DynamicInvocationHandler::class.java)
}
}
@JvmStatic
fun main(args: Array<String>) {
val proxy = Proxy.newProxyInstance(
Playground::class.java.classLoader,
arrayOf<Class<*>>(MutableMap::class.java),
DynamicInvocationHandler()) as MutableMap<String, String>
proxy["foo"] = "bar"
}
}
and running this will print Invoked method: put
. How can I do something like this in a Kotlin common project?
Edit: I'm not trying to use anything from Java in my common module. I know how common projects work. What I'm interested in instead is whether there is a Kotlin-based solution for this or not.
Edit 2: I'm not trying to proxy the Map
class. I'm looking for something like Proxy
in the JDK which I can use to proxy any interface. Sorry for the confusion.
回答1:
I think the simple answer is that Kotlin Multi Platform reflection doesn't support proxies. You can use @KamiSempai's expect
- actual
solution when using the common module in a java application but you will need to find alternatives for JS and native targets.
回答2:
Probably Expect/Actual Factory should solve the problem.
Common Code:
interface ProxyMethod {
val name: String
// other properties
}
interface ProxyHandler {
fun invoke(proxy: Any, method: ProxyMethod, args: Array<Any>): Any
}
expect object Logger {
fun info(message: String, vararg arguments: Any)
}
expect object ProxyFactory {
fun mutableMapWithProxy(handler: ProxyHandler): MutableMap<String, String>
}
private class ProxyHandlerImpl: ProxyHandler {
override fun invoke(proxy: Any, method: ProxyMethod, args: Array<Any>): Any {
Logger.info("Invoked method: {}", method.name)
return 0
}
}
object Common {
fun doSomething() {
val myMap = ProxyFactory.mutableMapWithProxy(ProxyHandlerImpl())
myMap["foo"] = "bar"
}
}
Java Code:
actual object Logger {
private val instance = LoggerFactory.getLogger(
DynamicInvocationHandler::class.java)
actual fun info(message: String, vararg arguments: Any) {
instance.info(message, *arguments)
}
}
actual object ProxyFactory {
actual fun mutableMapWithProxy(handler: ProxyHandler): MutableMap<String, String> {
return Proxy.newProxyInstance(
Playground::class.java.classLoader,
arrayOf<Class<*>>(MutableMap::class.java),
ProxyHandlerAdapter(handler)) as MutableMap<String, String>
}
}
class ProxyHandlerAdapter(private val handler: ProxyHandler) : InvocationHandler {
@Throws(Throwable::class)
override operator fun invoke(proxy: Any, method: Method, args: Array<Any>): Any {
return handler.invoke(proxy, methodToProxyMethod(method), args)
}
fun methodToProxyMethod(method: Method): ProxyMethod {
// convert Method to ProxyMethod
}
}
@JvmStatic
fun main(args: Array<String>) {
Common.doSomething()
}
Unfortunately I don't know any library that simplify this work so you should do this by hand for each platform and interface.
回答3:
There is no equivalent for this in current Kotlin Native versions. Looking at the other answers, they seem to have actual types in the expect/actuals, but the purpose of a live proxy is to supply a type at runtime and generate a compatible instance that can be delegated to.
This is how things like Retrofit work. Rather than source gen, the interface definitions are proxied internally.
For now, you'll need to do source-gen, as far as I know. That's for native. Not sure about JS.
来源:https://stackoverflow.com/questions/54327143/how-to-create-a-dynamic-proxy-in-kotlin-common-code