scala - 我可以避免在这种情况下使用结构类型吗?
问题描述
我有一些代码同时使用第三方库和我自己的库。在我自己的库中,我不想依赖第三方库,所以我希望我的方法之一接受更通用的类型作为参数。不幸的是,我无法将特征扩展或混合到第 3 方类,因为它们是使用工厂方法生成的,并且类是final
.
我可以通过使用结构类型来解决这个问题,但我想知道是否有替代方案?如果可能,我不想遍历工厂方法返回的每条记录和单独类型的“新”实例。
我把它归结为如下场景:
无法更改的第三方库代码
// Class inside library cannot be extended due to it being 'final'
final class SpecificRecord(val values: IndexedSeq[String]) {
def get(i: Int): String = {
values(i)
}
}
// A companion object simply to create some sample data in an iterator
object SpecificRecord{
def generateSpecificRecords(): Iterator[SpecificRecord] = new Iterator[SpecificRecord] {
var pointerLocation: Int = 0
private val state = IndexedSeq(
IndexedSeq("Row1 Col1", "Row1 Col2", "Row 1 Col3"),
IndexedSeq("Row2 Col1", "Row2 Col2", "Row 2 Col3")
)
override def hasNext: Boolean = {
if (pointerLocation < state.length) true else false
}
override def next(): SpecificRecord = {
val record = new SpecificRecord(state(pointerLocation))
pointerLocation += 1
record
}
}
}
正如你在上面看到的,SpecificRecord
类是最终的,而specificRecords
val 是一个迭代器,里面有一堆SpecificRecord
。如果可能的话,我不想遍历每个specificRecord
对象并创建一个新的、更通用的对象。
我可以更改的代码
val specificRecords: Iterator[SpecificRecord] = SpecificRecord.generateSpecificRecords()
type gettable = {
def get(i: Int): String
}
def printRecord(records: Iterator[gettable]): Unit = {
for (record <- records) {
println(record.get(0), record.get(1), record.get(2))
}
}
printRecord(specificRecords)
这正确打印:
(Row1 Col1,Row1 Col2,Row 1 Col3)
(Row2 Col1,Row2 Col2,Row 2 Col3)
我有一个printRecord
方法并不真正关心传入的是什么类型,只要它有一个类似get(Int): String
. 这是一个相当不错的解决方案,但我想知道是否可以在没有结构类型的情况下做到这一点?
解决方案
这是类型类的典型用例。
trait Gettable[T] {
def get(t: T, i: Int): String
}
object Gettable {
implicit object SpecificRecordGettable extends Gettable[SpecificRecord] {
def get(sr: SpecificRecord, i: Int) = sr.get(i)
}
}
def printRecord[T : Gettable](records: Iterator[T]) = {
val getter = implicitly[Gettable[T]]
records.foreach { record =>
println(getter.get(record, 0), getter.get(record, 1), getter.get(record, 2))
}
}
这比您使用结构化类型的方法更冗长:对于您想要成为的每种类型gettable
,您必须添加一个实现 的隐式对象get
,但它无需反射即可工作,这是一件好事。
这种方法的另一个优点是它的灵活性:底层类型不必get
特别具有,你可以用隐式实现任何东西。例如:
implicit object ArrayGettable extends Gettable[Array[String]] {
def get(a: Array[String], i: Int) = a(i)
}
implicit object ProductGettable extends Gettable[Product] {
def get(p: Product, i: Int) = p.productIterator.drop(i).next.toString
}
现在,您printRecord
也可以使用字符串数组(只要它们至少包含三个元素),甚至是元组和案例类。尝试这个:
printRecord[Product](Iterator((1,2, "three"), ("foo", "bar", 5)))
或这个:
case class Foo(x: String, y: Int, z: Seq[Int])
printRecord[Product](Iterator(Foo("bar", 1, 1::2::Nil), ("foo", "bar", "baz")))
一个类似但不那么冗长的方法是只定义一个隐式的“getter”而不用关心类型类:
def printRecord[T](records: Iterator[T])(implicit getter: (T,Int) => String) =
records.foreach { record =>
println(getter(record, 0), getter(record, 1), getter(record, 2))
}
object Getters {
implicit def getter(sr: SpecificRecord, i: Int) = sr.get(i)
implicit def getter(a: Array[String], i: Int) = a(i)
implicit def getter(p: Product, i: Int) = p.productIterator.drop(i).next.toString
}
这在用法上是相当等价的,不同之处在于 type 类允许您潜在地定义多个方法,但如果您只需要get
,那么这将为您节省一些击键。
推荐阅读
- parallel-processing - 有什么方法可以减少 CUDA 中数组的总和 100M 浮点元素?
- c# - Array.reverse 反转当前数组和初始数组,但为什么呢?
- sql - Postgres中基于最后一个非空值的唯一约束
- python - 预测总是 1 或 0
- c - 如何理解 xv6 引导代码中的以下代码?
- reactjs - React 服务器端渲染页面源未更新
- go - Goroutine 循环未完成
- reactjs - 将文件存储到 Laravel 的公共存储(ReactJS 和 Laravel)
- python - 如何在 Keras ConvLSTM 层中处理输入数据
- node.js - 在 Visual Studio 中构建 MS Word 插件时使用 NodeJS 服务器而不是本地 IIS 服务器