如何通过反射收集类成员用作后续参数?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 infieldOrder—make sure these names match exactly!- We cache the reflection references and write functions in
membersandaddFunctionsso we only do the heavy reflection work once (thanks to theinitializedflag).
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
Bufferizabletoo, then add a case in thewhenblock to recursively call theirtomethod. - Performance: The
initializedflag ensures reflection only runs once per instance—critical if you're writing the same object multiple times.
内容的提问来源于stack exchange,提问作者elect
相关产品推荐
相关产品推荐

