我想具有各种组件的“口味”,每一个处理一个不同的“线”的形式(例如字符串,字节数组等)。下面的实施例。在read()函数的内部结构并不重要。
请注意,在使用我需要强制转换参数"Heavy"
到thing.WIRE
工作。由于这是我的最高级别的API,我不希望用户必须强制转换。他们选择当他们打电话FantasticThing.apply
的味道(或接受默认)。从那以后,我宁愿不再需要强制转换。
我怎样才能避免施放并斯卡拉意识到read()
说法是基于StringFlavor
被选择的String?
trait Flavor {
type WIRE
def read[T](wire: WIRE)(implicit tt: TypeTag[T]): T
}
trait Maker {
def make(): Flavor
}
object StringFlavor extends Maker {
def make(): Flavor { type WIRE = String } = StringFlavor()
}
case class StringFlavor() extends Flavor {
type WIRE = String
def read[T](wire: String)(implicit tt: TypeTag[T]): T = {
println(tt.tpe)
if(tt.tpe =:= typeOf[Int]) {
5.asInstanceOf[T]
} else
throw new Exception("Boom")
}
}
object FantasticThing {
def apply[WIRE](maker: Maker = StringFlavor): Flavor = maker.make()
}
object RunMe extends App {
val thing: Flavor = FantasticThing(StringMaker)
println(thing.read[Int]("Heavy".asInstanceOf[thing.WIRE])) // <-- How can I avoid this cast?
}
如果我提供了一堆口味的,那么用户应该能够做这样的事情:
val foo = FantasticThing(ByteArrayFlavor)
您可以WIRE
类型参数,并通过类型成员或者您Maker
型传播它。即:
import scala.reflect.runtime.universe._
trait Flavor[WIRE] {
def read[T](wire: WIRE)(implicit tt: TypeTag[T]): T
}
trait Maker {
type O
def make(): Flavor[O]
}
object StringMaker extends Maker {
type O = String
def make(): Flavor[O] = StringFlavor()
}
case class StringFlavor() extends Flavor[String] {
def read[T](wire: String)(implicit tt: TypeTag[T]): T = {
if(tt.tpe =:= typeOf[Int]) {
5.asInstanceOf[T]
} else
throw new Exception("Boom")
}
}
object FantasticThing {
def apply(): Flavor[String] = StringMaker.make()
def apply(maker: Maker): Flavor[maker.O] = maker.make() // Path dependent type.
}
object RunMe extends App {
val thing: Flavor[String] = FantasticThing(StringMaker)
thing.read[Int]("Heavy") // res0: Int = 5
}
编辑:增加了无参数适用()这个anwser。如果用机的默认值(例如StringMaker)你会得到一个编译错误,因为参数“重”,现在应该是类型Maker.O。添加不带参数的应用解决了这个问题,同时提供给调用者相同的体验。
我冒昧地修改代码的意图来说明如何(我是这么理解的),您的问题可以使用类型类和类型的参数来解决,而不是类型成员。
import scala.reflect.runtime.universe.{TypeTag, typeOf}
implicit class Json(val underlying: String) extends AnyVal
implicit class Csv(val underlying: String) extends AnyVal
trait Flavor[W] {
def read[T](wire: W)(implicit tt: TypeTag[T]): T
}
trait Maker[W] {
def make(): Flavor[W]
}
object Maker {
implicit val StringFlavorMaker: Maker[String] = new Maker[String] {
override def make(): Flavor[String] = StringFlavor
}
implicit val JsonFlavorMaker: Maker[Json] = new Maker[Json] {
override def make(): Flavor[Json] = JsonFlavor
}
implicit val CsvFlavorMaker: Maker[Csv] = new Maker[Csv] {
override def make(): Flavor[Csv] = CsvFlavor
}
}
case object StringFlavor extends Flavor[String] {
override final def read[T](wire: String)(implicit tt: TypeTag[T]): T = {
if(tt.tpe =:= typeOf[Int])
0.asInstanceOf[T]
else
throw new Exception("Boom 1")
}
}
case object JsonFlavor extends Flavor[Json] {
override final def read[T](wire: Json)(implicit tt: TypeTag[T]): T = {
if(tt.tpe =:= typeOf[Int])
3.asInstanceOf[T]
else
throw new Exception("Boom 2")
}
}
case object CsvFlavor extends Flavor[Csv] {
override final def read[T](wire: Csv)(implicit tt: TypeTag[T]): T = {
if(tt.tpe =:= typeOf[Int])
5.asInstanceOf[T]
else
throw new Exception("Boom 3")
}
}
object FantasticThing {
def apply[W](implicit maker: Maker[W]): Flavor[W] = maker.make()
}
然后,您可以创建和用户的任何味道(因为有范围的隐含制造商)这种方式。
val stringFlavor = FantasticThing[String]
// stringFlavor: Flavor[String] = StringFlavor
stringFlavor.read[Int]("Heavy")
// res0: Int = 0
val jsonFlavor = FantasticThing[Json]
// jsonFlavor: Flavor[Json] = JsonFlavor
jsonFlavor.read[Int]("{'heavy':'true'}")
// res1: Int = 3
val csvFlavor = FantasticThing[Csv]
// csvFlavor: Flavor[Csv] = CsvFlavor
csvFlavor.read[Int]("Hea,vy")
// res2: Int = 0
在一般情况下,最好是留过类型的成员,因为它们更复杂,用于诸如路径依赖的类型更先进的东西。 让我知道在评论,如果您有任何疑问。
免责声明:我是坏与类型成员(仍在学习他们),这可能促使我用不同的选择。 - 在任何情况下,我希望你可以申请类似你真正的问题东西..