You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过反射收集类成员用作后续参数?Bufferizable类自动写入ByteBuffer咨询

Got it, let's break down how to tackle your two core goals: auto-writing Bufferizable subclasses to a ByteBuffer, and using reflection to collect class members for this workflow.


1. First: Reflection to Collect Class Members

To map your subclass fields to operations that write to ByteBuffer, you'll use Kotlin's reflection API to fetch properties matching the fieldOrder you define. Here's how to fill in that this::class.... gap in your code:

First, add necessary imports if you haven't already:

import kotlin.reflect.KProperty
import java.nio.ByteBuffer

Then, in your Bufferizable abstract class, we'll formalize the reflection logic to collect members and bind them to write functions:

abstract class Bufferizable {
    abstract val fieldOrder: Array<String>
    // Stores functions that write each field to ByteBuffer
    private lateinit var addFunctions: Array<((ByteBuffer) -> Unit)?>
    // Stores reflection references to each class member
    private lateinit var members: Array<KProperty<*>?>
    var initialized = false

    open fun size(): Int {
        // Calculate total byte size of all fields (customize based on your types)
        return members.filterNotNull().sumOf { prop ->
            when (prop.returnType.classifier) {
                Int::class -> 4
                Long::class -> 8
                String::class -> (prop.get(this) as String).toByteArray().size + 4 // +4 for length prefix
                Byte::class -> 1
                Short::class -> 2
                Float::class -> 4
                Double::class -> 8
                else -> throw IllegalArgumentException("Unsupported type: ${prop.returnType}")
            }
        }
    }

    open infix fun to(buffer: ByteBuffer) {
        if (!initialized) {
            addFunctions = Array(fieldOrder.size) { null }
            members = Array(fieldOrder.size) { null }
            
            fieldOrder.forEachIndexed { i, fieldName ->
                // Fetch the property via reflection, matching the field name
                val member = this::class.memberProperties.find { it.name == fieldName }
                    ?: throw IllegalArgumentException("Field '$fieldName' not found in ${this::class.simpleName}")
                members[i] = member

                // Bind a write function based on the field's type
                addFunctions[i] = when (member.returnType.classifier) {
                    Int::class -> { buf -> buf.putInt(member.get(this) as Int) }
                    Long::class -> { buf -> buf.putLong(member.get(this) as Long) }
                    String::class -> { buf ->
                        val bytes = (member.get(this) as String).toByteArray()
                        buf.putInt(bytes.size) // Write length first
                        buf.put(bytes)
                    }
                    Byte::class -> { buf -> buf.put(member.get(this) as Byte) }
                    Short::class -> { buf -> buf.putShort(member.get(this) as Short) }
                    Float::class -> { buf -> buf.putFloat(member.get(this) as Float) }
                    Double::class -> { buf -> buf.putDouble(member.get(this) as Double) }
                    // Add custom types here (e.g., other Bufferizable subclasses)
                    Bufferizable::class -> { buf -> (member.get(this) as Bufferizable).to(buf) }
                    else -> throw IllegalArgumentException("Unsupported type for field '$fieldName'")
                }
            }
            initialized = true
        }

        // Execute all pre-bound write functions
        addFunctions.filterNotNull().forEach { it(buffer) }
    }
}

Key Reflection Details:

  • this::class.memberProperties: Fetches all public properties of the subclass instance.
  • find { it.name == fieldName }: Matches the property to the name defined in fieldOrder—make sure these names match exactly!
  • We cache the reflection references and write functions in members and addFunctions so we only do the heavy reflection work once (thanks to the initialized flag).

2. Example Usage with a Subclass

Let's create a concrete subclass to see how this works end-to-end:

data class Player(
    val userId: Int,
    val username: String,
    val level: Short,
    val highScore: Long
) : Bufferizable() {
    // Define the exact order we want fields written to ByteBuffer
    override val fieldOrder = arrayOf("userId", "username", "level", "highScore")
}

// Test it out
fun main() {
    val player = Player(1001, "LunaStar", 15, 567890L)
    val buffer = ByteBuffer.allocate(player.size())
    
    // Write the player data to the buffer
    player to buffer
    
    // Reset buffer to read mode and verify the data
    buffer.flip()
    println(buffer.int) // 1001
    val nameLength = buffer.int
    val nameBytes = ByteArray(nameLength)
    buffer.get(nameBytes)
    println(String(nameBytes)) // LunaStar
    println(buffer.short) // 15
    println(buffer.long) // 567890
}

3. Customization Tips
  • Nullable Fields: If your subclass has nullable properties, add null checks in the write functions (e.g., write a Byte(1) for non-null, Byte(0) for null, then the value if present).
  • Custom Types: For your own data classes, have them implement Bufferizable too, then add a case in the when block to recursively call their to method.
  • Performance: The initialized flag ensures reflection only runs once per instance—critical if you're writing the same object multiple times.

内容的提问来源于stack exchange,提问作者elect

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 10:03:52