我有一些代码同时使用第三方库和我自己的库。在我自己的库中,我不想依赖第三方库,所以我希望我的方法之一接受更通用的类型作为参数。不幸的是,我无法将特征扩展或混合到第 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
类是 final 类,而 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
这样的方法。这是一个相当不错的解决方案,但我想知道是否可以在没有结构类型的情况下做到这一点?
答案 0 :(得分:2)
这是类型类的典型用例。
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
也适用于字符串数组(只要它们至少有三个元素),甚至元组和 case 类。
试试这个:
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
}
这在用法上相当等效,不同之处在于类型类允许您定义多个方法,但如果您只需要 get
,那么这将为您节省几次按键操作。