具有不同隐式库的Scala工厂方法

Aiv*_*ras 0 factory scala implicit

我想支持Json和Parquet文件格式。客户端不必关心其实现,但是必须传递一种类型来标识格式。

到目前为止,我有两个具有此类签名的类:

class ParquetFileWriter[T](val path: String)(implicit val writer: ParquetWriter[T]) extends FileWriter[T]
Run Code Online (Sandbox Code Playgroud)

class JsonFileWriter[T](val path: String)(implicit writer: JsonWriter[T]) extends FileWriter[T]
Run Code Online (Sandbox Code Playgroud)

他们扩展的特征:

trait FileWriter[T] {
  def write(items: Seq[T]): Unit
}
Run Code Online (Sandbox Code Playgroud)

我想创建一个工厂类来通过参数构建类:

class Factory {
  def of[T](format: Format): FileWriter[T] = {
    format match {
      case ParquetSpark =>
        new ParquetFileWriter[T](defaultPath)

      case Json =>
        new JsonFileWriter[T](defaultPath)
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

问题是ParquetFileWriter和JsonFileWriter需要隐式变量(当它们从spray.json和com.github.mjakubowski84.parquet4s库到达时,它们不在我的控制范围之内。

如果格式依赖于不同的隐式函数,如何实现工厂格式?我在编译时收到“找不到隐式值”。

Dmy*_*tin 6

如果您同时具有两个隐式,仅添加上下文边界就足够了

class Factory {
  def of[T: ParquetWriter : JsonWriter](format: Format): FileWriter[T] = {
    format match {
      case ParquetSpark =>
        new ParquetFileWriter[T]("defaultPath")

      case Json =>
        new JsonFileWriter[T]("defaultPath")
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

如果您可能只有这两个隐式函数之一,则尝试创建Factory一个类型类

trait Factory[T] {
  def of(format: Format): FileWriter[T]
}

trait LowPriorityFactories {
  implicit def parquet[T: ParquetWriter]: Factory[T] = {
    case ParquetSpark => new ParquetFileWriter[T]("defaultPath")
    case _            => throw new Exception
  }

  implicit def json[T: JsonWriter]: Factory[T] = {
    case Json => new JsonFileWriter[T]("defaultPath")
    case _    => throw new Exception
  }
}

object Factory extends LowPriorityFactories {
  def of[T](format: Format)(implicit factory: Factory[T]): FileWriter[T] = factory.of(format)

  implicit def jsonParquet[T: JsonWriter : ParquetWriter]: Factory[T] = {
    case ParquetSpark => new ParquetFileWriter[T]("defaultPath")
    case Json         => new JsonFileWriter[T]("defaultPath")
  }
}
Run Code Online (Sandbox Code Playgroud)

现在,如果您同时具有这两个隐式,则适用于JsonParquetSpark

implicit val jsonWriter: JsonWriter[Int] = null
implicit val parketWriter: ParquetWriter[Int] = null
Factory.of[Int](Json)
Factory.of[Int](ParquetSpark)
Run Code Online (Sandbox Code Playgroud)

如果您仅具有隐式功能,JsonWriter则仅适用于Json

implicit val jsonWriter: JsonWriter[Int] = null
Factory.of[Int](Json)
Factory.of[Int](ParquetSpark) // Exception
Run Code Online (Sandbox Code Playgroud)

如果您仅具有隐式功能,ParquetWriter则仅适用于ParquetSpark

implicit val parketWriter: ParquetWriter[Int] = null
Factory.of[Int](Json) // Exception
Factory.of[Int](ParquetSpark)
Run Code Online (Sandbox Code Playgroud)

另一种选择是使选择更具类型级别,选择从运行时转移到编译时(然后,您根本不需要模式匹配,并且编译错误代替了运行时异常)。

sealed trait Format
case object ParquetSpark extends Format
type ParquetSpark = ParquetSpark.type
case object Json extends Format
type Json = Json.type

trait Factory[T, F <: Format] {
  def of: FileWriter[T]
}

object Factory {
  def of[T, F <: Format](implicit factory: Factory[T, F]): FileWriter[T] = factory.of

  implicit def parquet[T: ParquetWriter]: Factory[T, ParquetSpark] = new Factory[T, ParquetSpark] {
    override def of: FileWriter[T] = new ParquetFileWriter[T]("defaultPath")
  }

  implicit def json[T: JsonWriter]: Factory[T, Json] = new Factory[T, Json] {
    override def of: FileWriter[T] = new JsonFileWriter[T]("defaultPath")
  }
}

{
  implicit val jsonWriter: JsonWriter[Int] = null
  implicit val parketWriter: ParquetWriter[Int] = null
  Factory.of[Int, Json]
  Factory.of[Int, ParquetSpark]
}

{
  implicit val jsonWriter: JsonWriter[Int] = null
  Factory.of[Int, Json]
//  Factory.of[Int, ParquetSpark] // doesn't compile
}

{
  implicit val parketWriter: ParquetWriter[Int] = null
//  Factory.of[Int, Json] // doesn't compile
  Factory.of[Int, ParquetSpark]
}
Run Code Online (Sandbox Code Playgroud)