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

How to implement a trait with a generic case class that creates a dataset in Scala

我想创建一个 Scala trait,它应该用 case class T 来实现。trait 只是加载数据并将其转换为 T 类型的 Spark 数据集。我得到的错误是 no编码器可以存储,我认为这是因为Scala不知道T应该是一个caseclass。我怎么能告诉编译器呢?我在某个地方看到我应该提到产品,但是没有这样的 class 定义。请随意提出其他方法来做到这一点!

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

我正在使用 Spark 1.6.1

代码:

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.sql.{Dataset, SQLContext}    

/**
      * A trait that moves data on Hadoop with Spark based on the location and the granularity of the data.
      */
    trait Agent[T] {
      /**
        * Load a Dataframe from the location and convert into a Dataset
        * @return Dataset[T]
        */
      protected def load(): Dataset[T] = {
        // Read in the data
        SparkContextKeeper.sqlContext.read
          .format("com.databricks.spark.csv")
          .load("/myfolder/" + location + "/2016/10/01/")
          .as[T]
      }
    }

您需要执行两个操作:

  1. 在您的导入中添加 import sparkSession.implicits._
  2. 塑造你的特质trait Agent[T <: Product]

您的代码缺少 3 个东西:

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

不幸的是,您不能在 trait 中添加具有 context bounds 的类型参数,因此最简单的解决方法是使用abstract class 改为:

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 放在 对象 中并将类型参数移动到方法中:

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

哪个更可取主要取决于您要调用的位置和方式 load 以及您打算如何处理结果。