实现一个kotlin泛型函数,该函数从复杂的Java对象中检索特定字段

时间:2018-08-09 10:37:55

标签: java kotlin type-conversion wrapper

我正在编写一个Kotlin程序,该程序在内部使用Java库的一些功能(我无法修改)。 该函数之一(被称为“ feval”)在被调用时将返回具有以下结构的对象:是大小为1的Object的数组,其唯一成员是com.mathworks.matlab.types的实例。结构。该结构的每个字段还是一个大小为1的数组,其唯一成员是一个变量,类型可以是int,double,Array等...这就是我感兴趣的值。

我想用一个类包装这种混乱,该类提供直接访问“有趣”值的方法。这是我的照片:

class ExecutionResult (fevalOutput: Array<Any>) {
private val rootObj = fevalOutput[0]
val rootStruct: Struct
init {
    if (rootObj is Struct) {
        rootStruct = rootObj
    }
    else {
        throw ExecutionResultException("The output doesn't match the expected format")
    }
}

fun _get (field: String) : Any {
    val container = rootStruct.get(field)
            ?: throw NoSuchFieldException("The requested field cannot be found in the result")
    if (container is Array<*>) {
        val value = container[0]
        if (value != null) {
            return value
        }
        else {
            throw NoSuchFieldException("The requested field cannot be found in the result")
        }
    }
    else{
        throw ExecutionResultException("The requested field has an unexpected format")
    }
}
inline fun <reified T> get (field: String) : T {
    val value = _get(field)
    if (value is T) {
        return value
    }
    else {
        throw ExecutionResultException(
                "The requested field is not a member of the specified class: ${T::class.java}," +
                        " but a member of: ${value::class.java}")
    }
}}

现在,从我的代码中,我可以执行以下操作:

val output = engine.feval<Array<Any>>(1,functionName, *transmissionParameters.getParameters())
        val result = ExecutionResult(output)
        val simpleValue = result.get<Double>("simpleValue"))

基本上是我想要的。 但是,这并不总是有效。例如,有时我要查找的嵌套值是double数组的数组。如果我这样做:

val moreComplexValue = result.get<Array<Array<Double>>>("moreComplexValue"))

我生成以下异常:

Exception in thread "main" matlabdriver.ExecutionResultException: The requested field is not a member of the specified class: class [[Ljava.lang.Double;, but a member of: class [[D

如果我理解正确,则该问题是由于我试图将Java本机double数组转换为Double(类)数组而造成的

有人对如何解决这个问题和/或总体上改善我的包装器类有建议吗?

1 个答案:

答案 0 :(得分:2)

问题在于类型擦除不能保留泛型的嵌套类型。就您而言,Array<Any>将产生Array::class.javaArray<Array<Double>>也将产生结果。即使使用类型化的类型,该函数内部也只有顶级类可用。

Jackson(JSON序列化)库在反序列化为List<MyClass>之类的通用类型时会遇到相同的问题。它们使您可以显式指定类型参数:

// represents the generic type Outer<Inner>
val type: JavaType = mapper.typeFactory.constructParametricType(Outer::class.java, Inner::class.java)

我建议您提供一种类似的机制来提供层次结构中的所有类型。也许告诉自己有关Jackson的JavaType的信息,以了解其机制。使用Kotlin,您仍然可以使用带有修饰类型的通用语法,例如:

val type = dim3<Array, Array, Double>()
// or easier:
val type = array2<Double>()

然后您可以将该类型(将通用类型参数存储为类对象)传递为函数的第一个参数:

feval(type, otherArgs...)

type本身可以是具有许多工厂功能的data class MatlabType(val subtypes: List<KClass>),例如array2()