如何使用在Scala中创建数据集的通用案例类实现特征

时间:2016-11-10 15:38:08

标签: scala generics apache-spark-sql traits case-class

我想创建一个应该用案例类T实现的Scala特征。特性只是加载数据并将其转换为类型为T的Spark数据集。我得到的错误是没有编码器可以存储,我想是因为Scala不知道T应该是一个案例类。我该如何告诉编译器?我已经在某个地方看到过我应该提到的产品,但是没有定义这样的类...请随意提出其他方法来做到这一点!

我有以下代码,但它没有编译错误:42:错误:无法找到存储在数据集中的类型的编码器。导入sqlContext.implicits._支持原始类型(Int,String等)和产品类型(case类) [INFO] .as [T]

我正在使用Spark 1.6.1

代码:

toDataUrl

2 个答案:

答案 0 :(得分:5)

您的代码缺少3件事:

  • 的确,您必须让编译器知道T是Product的子类(所有Scala案例类和元组的超类)
  • 编译器还需要实际案例类的TypeTagClassTag。 Spark会隐式使用它来克服类型擦除
  • 导入sqlContext.implicits._

不幸的是,您无法在 trait 中添加上下文边界的类型参数,因此最简单的解决方法是使用abstract class代替:< / p>

import scala.reflect.runtime.universe.TypeTag
import scala.reflect.ClassTag

abstract class Agent[T <: Product : ClassTag : TypeTag] {
  protected def load(): Dataset[T] = { 
    val sqlContext: SQLContext = SparkContextKeeper.sqlContext
    import sqlContext.implicits._
    sqlContext.read.// same... 
  }
}

显然,这并不等同于使用特征,并且可能表明这种设计不适合这项工作。另一种方法是将load放在对象中,并将type参数移到方法中:

object Agent {
  protected def load[T <: Product : ClassTag : TypeTag](): Dataset[T] = {
    // same...
  }
}

哪一个更受欢迎主要取决于您打算load的地点和方式,以及您计划对结果做些什么。

答案 1 :(得分:0)

您需要采取两项措施:

  1. 在导入中添加import sparkSession.implicits._
  2. 制作特质trait Agent[T <: Product]