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

Kotlin中如何通过扩展方法为DataInputStream添加小端序读取方法?

如何在DataInputStream的Kotlin扩展函数中获取底层InputStream?

你的场景我很熟悉:原来通过继承DataInputStream实现小端序读取时,能直接持有底层输入流,但改成扩展函数后,因为DataInputStream继承的FilterInputStream里的in字段是protected的——只有子类能直接访问,扩展函数不属于子类范畴,所以拿不到这个字段。

下面给你两个可行的解决方案,你可以根据需求选择:


方案一:用反射获取底层InputStream(复用POI的LittleEndian工具)

我们可以通过反射拿到FilterInputStream里的in字段,这样就能在扩展函数里继续用你原来的LittleEndian逻辑。为了性能,我们提前初始化反射字段,避免每次调用都重复反射操作:

import java.io.DataInputStream
import java.io.EOFException
import java.io.InputStream
import org.apache.poi.util.LittleEndian

// 提前通过反射获取FilterInputStream的protected字段"in",并设置可访问
private val underlyingInputStreamField = Class.forName("java.io.FilterInputStream")
    .getDeclaredField("in")
    .apply { isAccessible = true }

// 给DataInputStream加一个扩展工具方法,获取底层输入流
private fun DataInputStream.getUnderlyingStream(): InputStream {
    return underlyingInputStreamField.get(this) as InputStream
}

// 实现小端序读取的扩展函数
fun DataInputStream.readShortLittle(): Short {
    val inp = getUnderlyingStream()
    return try {
        LittleEndian.readShort(inp)
    } catch (e: LittleEndian.BufferUnderrunException) {
        throw EOFException()
    }
}

fun DataInputStream.readIntLittle(): Int {
    val inp = getUnderlyingStream()
    return try {
        LittleEndian.readInt(inp)
    } catch (e: LittleEndian.BufferUnderrunException) {
        throw EOFException()
    }
}

fun DataInputStream.readLongLittle(): Long {
    val inp = getUnderlyingStream()
    return try {
        LittleEndian.readLong(inp)
    } catch (e: LittleEndian.BufferUnderrunException) {
        throw EOFException()
    }
}

// 测试代码
fun main(args: Array<String>) {
    val inp = DataInputStream(System.`in`)
    while (true) {
        val i = try {
            inp.readIntLittle()
        } catch (e: EOFException) {
            break
        }
        println(i)
    }
}

注意点:

  • 反射依赖JDK的内部实现,虽然FilterInputStream的in字段几乎不会变,但如果未来JDK版本有极端变更,可能需要调整。
  • 我们把反射逻辑封装成私有扩展方法,让业务代码更整洁。

方案二:手动实现小端序读取(不依赖反射和POI工具)

如果你不想用反射,也可以直接用DataInputStream的read()方法手动拼出小端序的数值,这样代码更独立,兼容性也更好:

import java.io.DataInputStream
import java.io.EOFException

fun DataInputStream.readShortLittle(): Short {
    val byte1 = read()
    val byte2 = read()
    if (byte1 == -1 || byte2 == -1) throw EOFException()
    // 小端序是低字节在前,所以byte1是低位,byte2是高位
    return ((byte2 shl 8) or (byte1 and 0xFF)).toShort()
}

fun DataInputStream.readIntLittle(): Int {
    val byte1 = read()
    val byte2 = read()
    val byte3 = read()
    val byte4 = read()
    if (byte1 == -1 || byte2 == -1 || byte3 == -1 || byte4 == -1) throw EOFException()
    return (byte4 shl 24) or (byte3 shl 16) or (byte2 shl 8) or (byte1 and 0xFF)
}

fun DataInputStream.readLongLittle(): Long {
    val bytes = mutableListOf<Int>()
    repeat(8) {
        val b = read()
        if (b == -1) throw EOFException()
        bytes.add(b)
    }
    // 小端序:第一个字节是最低位,最后一个是最高位
    return (bytes[7].toLong() shl 56) or
            (bytes[6].toLong() shl 48) or
            (bytes[5].toLong() shl 40) or
            (bytes[4].toLong() shl 32) or
            (bytes[3].toLong() shl 24) or
            (bytes[2].toLong() shl 16) or
            (bytes[1].toLong() shl 8) or
            (bytes[0].toLong() and 0xFF)
}

// 测试代码
fun main(args: Array<String>) {
    val inp = DataInputStream(System.`in`)
    while (true) {
        val i = try {
            inp.readIntLittle()
        } catch (e: EOFException) {
            break
        }
        println(i)
    }
}

优点:

  • 完全不依赖外部工具类或反射,代码更稳定。
  • 直接使用DataInputStream的读取方法,和原有读取逻辑共享缓冲(比如底层是BufferedInputStream时,不会出现读取错位的问题)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:02:20