我正在为以下问题研究正确的语法和结构。
我有两个具有两个独立模式的数据集-分别称为ClientEvent
和ServerEvent
–存储在磁盘上。我正在使用的代码库定义了一个类Reader[T :< Asset]
,其中ClientEvent
和ServerEvent
是Asset
的子类型。 Asset
是一个特征。
我正在编写一个函数:
def getPathAndReader(config): (String, Reader[Asset]) = {
if (config.readClient) {
return getClientPathAndReader(config)
} else {
return getServerPathAndReader(config)
}
}
这不能在我的Scala代码中编译。据我了解,T
必须是Asset
的子类型,ServerEvent
和ClientEvent
均为Reader[ServerEvent] <: Reader[Asset]
。但是由于函数的输入是协变的,所以我编写的函数不能仅返回此较低类型,我必须将其转换为超类型吗?那会丢失太多信息吗?
load
是关于特征Asset
trait Reader[T <: Asset] {
def load(raw: DataFrame): Dataset[T]
}
构造此代码的另一种方法是什么?
该代码的目的是获取返回的文件路径,并调用Reader::load(filePath: String)
以取回数据。子类型的读取器具有一些内部逻辑,用于清除从磁盘检索到的数据,然后将其作为Dataframe
返回。这意味着它依赖于传入的类型。我来自C ++ / C#背景,因此我的想法是,如果您有通用的Reader[Asset]
但调用Reader::load(path: String)
,它将基于实际的类型,类似于Base* ptr
并调用派生方法。
答案 0 :(得分:2)
您声称
“据我了解,T
必须是Asset
的子类型,因此ServerEvent
和ClientEvent
都是Reader[ServerEvent] <: Reader[Asset]
。” 不正确。通常,如果A
和B
是普通类型,例如A <: B
和G[T]
是泛型类型,则所有3种情况都是可能的:
G[A] <: G[B]
-典型的例子是一些只读集合,例如Iterator
G[A] :> G[B]
-典型示例是某种使用者,例如函数T => ()
G[A]
和G[B]
不相关的不变情况。 T
的某些用法是协变而某些是协变的最典型情况。例如,简单的映射函数T => T
是不变的。同样,大多数可变集合也是不变的,因为“产生”和“消耗”两个对象。不幸的是,Dataset[T]
是不变的(而不是协变Dataset[+T]
或协变Dataset[-T]
)。这实际上使您的Reader
也不变。至于如何解决此问题,很难在不了解更大范围的情况下提出建议。例如,为什么您的getClientPathAndReader
和getServerPathAndReader
不返回Dataset[Asset]
?如果确实使用了特定的ServerEvent
和ClientEvent
,那么您的设计无论如何都不是类型安全的。如果仅使用Asset
,则更改读者以返回Dataset[Asset]
似乎是最简单的解决方案。