0

我有一个函数,它采用metadata两种可能类型的参数:Metadata1 或 Metadata2。我正在尝试编写一个可用于两者的实用程序函数,因为有大量的代码重用。它们都有一个 DataPoint 对象列表,每个对象都有 name() 方法。

我认为在我的代码中 Kotlin 会使用类型推断来知道它dataPoints只能是两种都具有 DataPoint.name() 方法的类型之一,但我得到的是“未解析的引用:名称”。我怎样才能使这个函数足够通用以适用于 Metadata1 和 Metadata2?

var dataPoints: List<Any>
if (metadata is Metadata1) {
    dataPoints = metadata.metadata1().dataPoints()
} else {
    dataPoints = (metadata as Metadata2).metadata2().dataPoints()
}
if (data.size > 1) {
    textView.test = dataPoints.fold("")
    { x, dataPoint ->
        x + dataPoint.name() + " \n" // unresolved reference: name
    }
}

4

1 回答 1

1

这不是 Kotlin 独有的,静态类型的 OOP 语言就是这样工作的。

您的 dataPoints 是类型List<Any>并且Any没有 name() 函数。你没有显示很多代码,所以我不知道你有什么样的对象。

这是关于运行时与编译时的关系。编译器无法预测您将List<Any>在运行时放入哪些类型,因此您可以对其成员调用的唯一函数是属于Any该类的函数。想象一下,如果该列表包含一个没有 name() 函数的对象。如果编译器允许您调用 name(),那么您将遇到运行时崩溃。这就是为什么您在尝试时会收到编译器时错误。

如果列表中有两种不同类型的对象,一种方法是创建一个接口,它们都使用接口中的共享方法实现。看起来像这样:

interface Name {
    fun name()
}

将 dataPoints 更改为List<Name>,让您的数据类实现它,现在可以dataPoint.name()编译,因为列表中允许的唯一对象是Name具有 name() 函数的类型的对象。

var dataPoints: List<Name>
if (metadata is Metadata1) {
    dataPoints = metadata.metadata1().dataPoints()
} else {
    dataPoints = (metadata as Metadata2).metadata2().dataPoints()
}
if (data.size > 1) {
    textView.test = dataPoints.fold("")
    { x, dataPoint ->
        x + dataPoint.name() + " \n" // Compiles now
    }
}

您的 Metadata1 和 Metadata2 类也有类似的问题,它们可能应该实现一个接口或扩展一个超类。

于 2020-08-19T19:22:47.287 回答