我希望组件具有各种“风味”,每个风味都处理不同的“接线”格式(例如,字符串,字节数组等)。下面的例子。read()函数的内在并不重要。
请注意,在使用我需要投参数"Heavy"
来thing.WIRE
工作。由于这是我的顶级API,因此我不希望用户强制转换。他们在致电时已选择口味FantasticThing.apply
(或接受默认值)。在那之后,我宁愿不需要演员。
如何避免强制转换并使Scala意识到read()
参数是基于被选择的字符串StringFlavor
?
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添加了无参数的apply()。如果使用maker的默认值(例如StringMaker),则会出现编译错误,因为参数“ Heavy”现在应该为Maker.O类型。添加无参数应用程序可以解决此问题,同时为调用者提供相同的体验。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句