如何在Kotlin中修复错误的构造函数参数

时间:2019-03-30 12:59:31

标签: kotlin reflection

我对Kotlin的思考有一个有趣的错误。

因此,即时通讯使用argTypes方法获取args的所有参数类型。

private fun argTypes(vararg args: Any): Array<Class<*>> {
        val argTypes = ArrayList<Class<*>>()
        args.forEach { argTypes.add(it::class.java) }
        return argTypes.toTypedArray()
    }

我这样使用它:

fun <T> newInstance(clazz: Class<*>, argTypes: Array<Class<*>>, vararg args: Any): T {
        return clazz.getDeclaredConstructor(*argTypes).newInstance(*args) as T
    }

最后:

ReflectionUtil.instance.newInstance<IBossBar>(
                PacketBossBar1_13_R2::class.java,
                TextComponent("asd"),Color.BLUE,Style.PROGRESS,100F)

我使用的是float参数'100F'。

当我使用该方法时,类型将为java.lang.Float,但是我的'PacketBossBar1_13_R2'构造函数具有如下所示的float参数:

constructor(
            message: TextComponent,
            color: Color,
            style: Style,
            progress: Float
    ): this(ComponentSerializer.toString(message), color, style, progress)

当我将构造函数作为手册时,它的返回结果

public io.github.utsukushihito.utsutil.nms.v1_13_R2.PacketBossBar1_13_R2(net.md_5.bungee.api.chat.TextComponent,io.github.utsukushihito.utsutil.api.bossbar.enums.Color,io.github.utsukushihito.utsutil.api.bossbar.enums.Style,float)

当我使用自动方式时,它会像这样返回NoSucMethodException:

java.lang.NoSuchMethodException: io.github.utsukushihito.utsutil.nms.v1_13_R2.PacketBossBar1_13_R2.<init>(net.md_5.bungee.api.chat.TextComponent, io.github.utsukushihito.utsutil.api.bossbar.enums.Color, io.github.utsukushihito.utsutil.api.bossbar.enums.Style, java.lang.Float)

还有我的ReflectionUtil和PackateBossBar类:

package io.github.utsukushihito.utsutil.api.misc

import org.bukkit.Bukkit
import java.lang.reflect.Field
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import java.util.*
import java.util.stream.Collectors

class ReflectionUtil {

    private val nmsVersion: String
        get() = nms().split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[3]

    val craftBukkitVersion: String
        get() = cb().split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[3]

    private fun nms(): String {
        return exec<Any>(Bukkit.getServer(), "getServer").javaClass.getPackage().name
    }

    private fun cb(): String {
        return Bukkit.getServer().javaClass.getPackage().name
    }

    fun getPackageName(nmsObject: Any): String {
        return nmsObject.javaClass.getPackage().name
    }

    fun getBukkitClass(craftObject: Any): Class<*> {
        var clazz: Class<*> = craftObject.javaClass
        while (clazz.canonicalName.contains(".craftbukkit.")) {
            clazz = clazz.superclass
        }
        return clazz
    }

    fun getCustomBukkitClass(className: String): Class<*> {
        return Class.forName("org.bukkit.craftbukkit.$nmsVersion.$className")
    }

    fun getNMSClass(name: String): Class<*> {
        return Class.forName("net.minecraft.server.$nmsVersion.$name")
    }


    fun <T> execStatic(clazz: Class<*>, methodName: String, vararg args: Any): T {
        val method = getMethod(clazz, methodName, *argTypes(*args))
        val wasAccessible = method.isAccessible
        method.isAccessible = true
        try {
            return method.invoke(null, *args) as T
        } finally {
            method.isAccessible = wasAccessible
        }
    }

    fun <T> execStatic(clazz: Class<*>, methodName: String, argTypes: Array<Class<*>>, vararg args: Any): T {
        val method = getMethod(clazz, methodName, *argTypes)
        val wasAccessible = method.isAccessible
        method.isAccessible = true
        try {
            return method.invoke(null, *args) as T
        } finally {
            method.isAccessible = wasAccessible
        }
    }

    fun <T> exec(obj: Any, methodName: String, argTypes: Array<Class<*>>, vararg args: Any): T {
        val aClass = obj.javaClass
        val method = getMethod(aClass, methodName, *argTypes)
        val wasAccessible = method.isAccessible
        method.isAccessible = true

        try {
            return method.invoke(obj, *args) as T
        } finally {
            method.isAccessible = wasAccessible
        }
    }

    fun getMethod(aClass: Class<*>, methodName: String, vararg argTypes: Class<*>): Method {
        return aClass.getDeclaredMethod(methodName, *argTypes)
    }

    fun findMethod(aClass: Class<*>, returnType: Class<*>, vararg argTypes: Class<*>): Method {
        return findMethods(aClass, returnType, *argTypes)[0]
    }

    fun findMethods(aClass: Class<*>, returnType: Class<*>, vararg argTypes: Class<*>): List<Method> {
        val methods = ArrayList<Method>()
        for (m in aClass.declaredMethods) {
            if (m.returnType == returnType && m.parameterTypes.size == argTypes.size) {
                val mLookup = aClass.getMethod(m.name, *argTypes)

                if (mLookup != null) methods.add(mLookup)
            }
        }
        return methods
    }

    fun <T> exec(obj: Any, methodName: String, vararg args: Any): T {
        return exec(obj, methodName, argTypes(*args), *args) as T
    }

    fun <T> getField(clazz: Class<*>, fieldName: String): T {
        val field = getFieldFromClass(clazz, fieldName)
        val wasAccessible = field.isAccessible
        field.isAccessible = true
        try {
            return field.get(null) as T
        } finally {
            field.isAccessible = wasAccessible
        }
    }

    fun <T> getField(obj: Any, fieldName: String): T {
        val field = getFieldInternal(obj, fieldName)
        val wasAccessible = field.isAccessible
        field.isAccessible = true
        try {
            return field.get(obj) as T
        } finally {
            field.isAccessible = wasAccessible
        }
    }

    fun getFieldInternal(obj: Any, fieldName: String): Field {
        return getFieldFromClass(obj.javaClass, fieldName)
    }

    fun getFieldFromClass(aClass: Class<*>, fieldName: String): Field {
        return try {
            aClass.getDeclaredField(fieldName)
        } catch (e: NoSuchFieldException) {
            try {
                aClass.getField(fieldName)
            } catch (e1: NoSuchFieldException) {
                getFieldFromClass(aClass.superclass, fieldName)
            }
        }
    }

    fun setField(obj: Any, fieldName: String, field: Any?) {
        val declaredField = getFieldInternal(obj, fieldName)
        val wasAccessible = declaredField.isAccessible
        declaredField.isAccessible = true
        try {
            declaredField.set(obj, field)
        } finally {
            declaredField.isAccessible = wasAccessible
        }
    }

    fun <T> newInstance(clazz: Class<*>, argTypes: Array<Class<*>>, vararg args: Any): T {
        return clazz.getDeclaredConstructor(*argTypes).newInstance(*args) as T
    }

    fun <T> newInstance(clazz: Class<*>, vararg args: Any): T {
        return newInstance(clazz, argTypes(*args), *args)
    }

    fun <T> newInstance(className: String, vararg args: Any): T {
        return newInstance(className, argTypes(*args), *args)
    }

    private fun argTypes(vararg args: Any): Array<Class<*>> {
        val argTypes = ArrayList<Class<*>>()
        args.forEach { argTypes.add(it::class.java) }
        return argTypes.toTypedArray()
    }

    fun dumpMethods(aClass: Class<*>): List<String> {
        val methods = aClass.declaredMethods
        val methodDescriptions = ArrayList<String>()
        val version = nmsVersion
        for (m in methods) {
            var parmString = Arrays.toString(Arrays.stream(m.parameterTypes).map<String>{ it.name }.toArray())
            parmString = parmString.substring(1, parmString.length - 1)
            var description = ((if (Modifier.isPublic(m.modifiers)) "public " else if (Modifier.isPrivate(m.modifiers)) "private " else "")
                    + (if (Modifier.isStatic(m.modifiers)) "static " else "")
                    + m.returnType + " " + m.name
                    + "(" + parmString + ")")
            description = description
                    .replace("class net.minecraft.server.$version.".toRegex(), "")
                    .replace("net.minecraft.server.$version.".toRegex(), "")
                    .replace("java.lang.".toRegex(), "")
            methodDescriptions.add(description)
        }
        val list = ArrayList<String>()

        list.add(aClass.toString().replace("class net.minecraft.server.$version.".toRegex(), "")
                .replace("net.minecraft.server.$version.".toRegex(), "")
                .replace("java.lang.".toRegex(), "") + ":")
        list.addAll(methodDescriptions.stream().sorted { obj, anotherString -> obj.compareTo(anotherString) }.collect(Collectors.toList()))
        return list
    }

    companion object {
        val instance = ReflectionUtil()
    }

}
package io.github.utsukushihito.utsutil.nms.v1_13_R2

import io.github.utsukushihito.utsutil.api.bossbar.addBossBarForPlayer
import io.github.utsukushihito.utsutil.api.bossbar.enums.Color
import io.github.utsukushihito.utsutil.api.bossbar.enums.Property
import io.github.utsukushihito.utsutil.api.bossbar.enums.Style
import io.github.utsukushihito.utsutil.api.bossbar.removeBossBarForPlayer
import io.github.utsukushihito.utsutil.api.misc.ReflectionUtil
import io.github.utsukushihito.utsutil.api.nms.IBossBar
import net.md_5.bungee.api.chat.TextComponent
import net.md_5.bungee.chat.ComponentSerializer
import net.minecraft.server.v1_13_R2.BossBattle
import net.minecraft.server.v1_13_R2.IChatBaseComponent
import net.minecraft.server.v1_13_R2.PacketPlayOutBoss
import org.bukkit.Location
import org.bukkit.craftbukkit.v1_13_R2.entity.CraftPlayer
import org.bukkit.entity.Player
import java.util.*

class PacketBossBar1_13_R2(
        private var message: String,
        private var color: Color,
        private var style: Style,
        private var progress: Float,
        vararg properties: Property
) : IBossBar{

    private val receivers = ArrayList<Player>()

    private val uuid = UUID.randomUUID()

    private var darkenSky: Boolean = false
    private var playMusic: Boolean = false
    private var createFog: Boolean = false
    private var visible: Boolean = false

    constructor(
            message: TextComponent,
            color: Color,
            style: Style,
            progress: Float,
            vararg properties: Property
    ): this(ComponentSerializer.toString(message), color, style, progress, *properties)

    // I Try to run this cotr with the reflection util.
    constructor(
            message: TextComponent,
            color: Color,
            style: Style,
            progress: Float
    ): this(ComponentSerializer.toString(message), color, style, progress)

    init {
        setMessage(message)
        setProgress(progress)
        properties.forEach {
            setProperty(it,true)
        }
    }

    override fun getPlayers(): Collection<Player> {
        return ArrayList(this.receivers)
    }

    override fun addPlayer(player: Player) {
        if (!receivers.contains(player)) {
            receivers.add(player)
            sendPacket(PacketPlayOutBoss.Action.ADD, player)
            player.addBossBarForPlayer(this)
        }
    }

    override fun removePlayer(player: Player) {
        if (receivers.contains(player)) {
            receivers.remove(player)
            sendPacket(PacketPlayOutBoss.Action.REMOVE, player)
            player.removeBossBarForPlayer(this)
        }
    }

    override fun getColor(): Color {
        return color
    }

    override fun setColor(color: Color) {
        if (color != this.color) {
            this.color = color;
            sendPacket(PacketPlayOutBoss.Action.UPDATE_STYLE, null);
        }
    }

    override fun getStyle(): Style {
        return style
    }

    override fun setStyle(style: Style) {
        if (style != this.style) {
            this.style = style
            sendPacket(PacketPlayOutBoss.Action.UPDATE_STYLE, null)
        }
    }

    override fun setProperty(property: Property, flag: Boolean) {
        when (property) {
            Property.DARKEN_SKY -> darkenSky = flag
            Property.PLAY_MUSIC -> playMusic = flag
            Property.CREATE_FOG -> createFog = flag
        }
        sendPacket(PacketPlayOutBoss.Action.UPDATE_PROPERTIES, null)
    }

    override fun setMessage(message: String) {
        if (!message.startsWith("{") || !message.endsWith("}")) {
            throw IllegalArgumentException("Invalid JSON")
        }
        if (message != this.message) {
            this.message = message
            sendPacket(PacketPlayOutBoss.Action.UPDATE_NAME, null)
        }
    }

    override fun getMessage(): String {
        return message;
    }

    override fun setVisible(flag: Boolean) {
        if (flag != this.visible) {
            this.visible = flag
            sendPacket(if (flag) PacketPlayOutBoss.Action.ADD else PacketPlayOutBoss.Action.REMOVE, null)
        }
    }

    override fun isVisible(): Boolean {
        return visible
    }

    override fun getProgress(): Float {
        return progress
    }

    override fun setProgress(progress: Float) {
        if (progress > 1) this.progress = progress / 100F

        if (progress != this.progress) {
            this.progress = progress
            sendPacket(PacketPlayOutBoss.Action.UPDATE_PCT, null)
        }
    }

    private fun sendPacket(action: PacketPlayOutBoss.Action, player: Player?) {
        try {
            val packet = PacketPlayOutBoss()
            val ref = ReflectionUtil.instance

            ref.setField(packet,"a",uuid)
            ref.setField(packet,"b",action)
            ref.setField(packet,"c",IChatBaseComponent.ChatSerializer.a(message)?: "")
            ref.setField(packet,"d",progress)
            ref.setField(packet,"e", BossBattle.BarColor.a(color.id))
            ref.setField(packet,"f",BossBattle.BarStyle.a(style.id))
            ref.setField(packet,"g",darkenSky)
            ref.setField(packet,"h",playMusic)
            ref.setField(packet,"i",createFog)

            if (player != null) {
                (player as CraftPlayer).handle.playerConnection.sendPacket(packet)
            } else {
                for (player1 in this.getPlayers()) {
                    (player1 as CraftPlayer).handle.playerConnection.sendPacket(packet)
                }
            }
        } catch (e: ReflectiveOperationException) {
            throw RuntimeException(e)
        }

    }

    override fun getMaxHealth(): Float {
        return 100F
    }

    override fun setHealth(percentage: Float) {
        setProgress(percentage / 100F)
    }

    override fun getHealth(): Float {
        return getProgress() * 100F
    }

    override fun getReceiver(): Player {
        throw UnsupportedOperationException()
    }

    override fun getLocation(): Location {
        throw UnsupportedOperationException()
    }

    override fun updateMovement() {
        throw UnsupportedOperationException()
    }

}```

1 个答案:

答案 0 :(得分:0)

您可以通过以下方式解决此特定情况

<?php
$name = $_POST['name'];
$num = $_POST['num'];
foreach ($num as $num){       
  foreach ($name as $name){ 
    $num * $name
  }
}       
?>

但是找不到一个private fun argTypes(vararg args: Any): Array<Class<*>> { val argTypes = args.map { it::class.javaPrimitiveType ?: it::class.java } return argTypes.toTypedArray() } 的构造函数。

简而言之:如果您知道构造函数/方法的实际参数,则签名中的类型有很多选项,因此,尝试一个(如java.lang.Float一样)通常是不可能的。除了您遇到的问题外,声明的参数类型还可以是参数类的超类型,构造函数可以具有varargs,等等。

一个可能的解决方案是获取所有构造函数,将参数提供给它们,并使用第一个不会引发异常的构造函数。您仍然需要注意varargs。性能成本是否可以接受取决于您的用例。