Scala 伴生对象可以有抽象成员吗?
Can a scala companion object have abstract members?
编辑:使问题更清楚
我有一个函数试图将 Map[String, String]
解析为 ReadRequests
和 WriteRequests
。我需要 MapConvert
可以从抽象 class Request
.
中隐式访问
// Conversion methods to make conversions from Map[String, String] to ReadRequest and WriteRequest implicit
trait MapConvert[A] {
def convert(values: Map[String, String]): A
}
object Map2ClassHelpers {
implicit class Map2Class(values: Map[String, String]) {
def extract[A](implicit mapper: MapConvert[A]): A = mapper convert values
}
}
// abstract Request class
abstract class Request[R <: Request[R]]() {
def companion: RequestCompanion[R]
}
trait RequestCompanion[R <: Request[R]] {
implicit val m: MapConvert[R]
}
// ReadRequest and companion object
case class ReadRequest(source: String, data: String)
extends Request[ReadRequest] {
override def companion: RequestCompanion[ReadRequest] = ReadRequest
}
object ReadRequest extends RequestCompanion[ReadRequest] {
implicit val m = new MapConvert[ReadRequest] {
def convert(values: Map[String, String]) = ReadRequest(
source = values("source"),
data = values("data")
)
}
}
// WriteRequest and companion object
case class WriteRequest(destination: String, data: String)
extends Request[WriteRequest] {
override def companion: RequestCompanion[WriteRequest] = WriteRequest
}
object WriteRequest extends RequestCompanion[WriteRequest] {
implicit val m = new MapConvert[WriteRequest] {
def convert(values: Map[String, String]) = WriteRequest(
destination = values("destination"),
data = values("data")
)
}
}
case class Spec(requestType: String, args: Map[String, String])
def fromMapToRequest[R <: Request[R]](spec: Spec)(implicit mR: Manifest[R]): R = {
/* this extract method comes from Map2ClassHelpers
* the result should be either a ReadRequest or a WriteRequest
* the error happens at this call saying "could not find implicit value for parameter mapper
* my guess is because it can't implicitly find the correct MapConvert from the abstract Request class
*/
// a bunch of checks ...
spec.args.extract[R]
}
def runExample() = {
val spec1: Spec = Spec("read", Map("source" -> "/readPath", "data" -> "ABC"))
val spec2: Spec = Spec("write", Map("destination" -> "/writePath", "data" -> "ABC"))
val specs = Seq(spec1, spec2)
specs.map(spec => {
spec.requestType match {
case "read" => fromMapToRequest[ReadRequest](spec)
case "write" => fromMapToRequest[WriteRequest](spec)
}
})
}
当我将其更改为:
def fromMapToRequest[R <: Request[R]](spec: Spec)(eval: spec => Request)(implicit mR: Manifest[R]): R = {
// a bunch of checks ...
import Map2ClassHelpers._
eval(spec)
}
def runExample() = {
val spec1: Spec = Spec("read", Map("source" -> "/readPath", "data" -> "ABC"))
val spec2: Spec = Spec("write", Map("destination" -> "/writePath", "data" -> "ABC"))
val specs = Seq(spec1, spec2)
import Map2ClassHelpers._
specs.map(spec => {
// this part is kind of ugly compared to the first way
spec.requestType match {
case "read" => fromMapToRequest[ReadRequest](spec)(_.args.extract[ReadRequest])
case "write" => fromMapToRequest[WriteRequest](spec)(_.args.extract[WriteRequest])
}
})
}
但我想要更通用的第一个选项,因此只需在 fromMapToRequest 中指定一次类型。
(可能由于问题编辑而过时)
为伴随对象定义特征:
trait FoobarCompanion[X <: Foobar[X]] {
implicit val m: Converter[X]
}
让伴随对象扩展它:
object Foo extends FoobarCompanion[Foo] {
implicit val m = Converter[Foo] { ... }
}
object Bar extends FoobarCompanion[Bar] {
implicit val m = Converter[Bar] { ... }
}
将方法 companion
添加到 Foobar
本身:
abstract class Foobar[F <: Foobar[F]] {
def companion: FoobarCompanion[F]
}
现在,只要通过 companion
.
获得 Foobar
的实例,您就可以访问正确的隐式
您可能想看看 GenericCompanion 周围的代码是如何组织的。
我不确定我是否正确理解了你的问题,但如果我理解了,问题似乎是你试图通过 implicit Manifest[R]
希望编译器能够找到正确的 MapConvert[R]
在伴随对象中,这种方法不起作用,因为 Manifest
没有扩展 implicit
参数的查找范围。但为什么不能将 MapConvert[R]
本身作为 implicit
参数传递给 fromMapToRequest
?当我将签名更改为
def fromMapToRequest[R <: Request[R]](spec: Spec)(implicit mc: MapConvert[R]): R = {
代码按我预期的方式编译和工作(在线查看 here)。如果您需要 Manifest[R]
进行其他检查,只需传递两个 implicit
参数即可。
编辑:使问题更清楚
我有一个函数试图将 Map[String, String]
解析为 ReadRequests
和 WriteRequests
。我需要 MapConvert
可以从抽象 class Request
.
// Conversion methods to make conversions from Map[String, String] to ReadRequest and WriteRequest implicit
trait MapConvert[A] {
def convert(values: Map[String, String]): A
}
object Map2ClassHelpers {
implicit class Map2Class(values: Map[String, String]) {
def extract[A](implicit mapper: MapConvert[A]): A = mapper convert values
}
}
// abstract Request class
abstract class Request[R <: Request[R]]() {
def companion: RequestCompanion[R]
}
trait RequestCompanion[R <: Request[R]] {
implicit val m: MapConvert[R]
}
// ReadRequest and companion object
case class ReadRequest(source: String, data: String)
extends Request[ReadRequest] {
override def companion: RequestCompanion[ReadRequest] = ReadRequest
}
object ReadRequest extends RequestCompanion[ReadRequest] {
implicit val m = new MapConvert[ReadRequest] {
def convert(values: Map[String, String]) = ReadRequest(
source = values("source"),
data = values("data")
)
}
}
// WriteRequest and companion object
case class WriteRequest(destination: String, data: String)
extends Request[WriteRequest] {
override def companion: RequestCompanion[WriteRequest] = WriteRequest
}
object WriteRequest extends RequestCompanion[WriteRequest] {
implicit val m = new MapConvert[WriteRequest] {
def convert(values: Map[String, String]) = WriteRequest(
destination = values("destination"),
data = values("data")
)
}
}
case class Spec(requestType: String, args: Map[String, String])
def fromMapToRequest[R <: Request[R]](spec: Spec)(implicit mR: Manifest[R]): R = {
/* this extract method comes from Map2ClassHelpers
* the result should be either a ReadRequest or a WriteRequest
* the error happens at this call saying "could not find implicit value for parameter mapper
* my guess is because it can't implicitly find the correct MapConvert from the abstract Request class
*/
// a bunch of checks ...
spec.args.extract[R]
}
def runExample() = {
val spec1: Spec = Spec("read", Map("source" -> "/readPath", "data" -> "ABC"))
val spec2: Spec = Spec("write", Map("destination" -> "/writePath", "data" -> "ABC"))
val specs = Seq(spec1, spec2)
specs.map(spec => {
spec.requestType match {
case "read" => fromMapToRequest[ReadRequest](spec)
case "write" => fromMapToRequest[WriteRequest](spec)
}
})
}
当我将其更改为:
def fromMapToRequest[R <: Request[R]](spec: Spec)(eval: spec => Request)(implicit mR: Manifest[R]): R = {
// a bunch of checks ...
import Map2ClassHelpers._
eval(spec)
}
def runExample() = {
val spec1: Spec = Spec("read", Map("source" -> "/readPath", "data" -> "ABC"))
val spec2: Spec = Spec("write", Map("destination" -> "/writePath", "data" -> "ABC"))
val specs = Seq(spec1, spec2)
import Map2ClassHelpers._
specs.map(spec => {
// this part is kind of ugly compared to the first way
spec.requestType match {
case "read" => fromMapToRequest[ReadRequest](spec)(_.args.extract[ReadRequest])
case "write" => fromMapToRequest[WriteRequest](spec)(_.args.extract[WriteRequest])
}
})
}
但我想要更通用的第一个选项,因此只需在 fromMapToRequest 中指定一次类型。
(可能由于问题编辑而过时)
为伴随对象定义特征:
trait FoobarCompanion[X <: Foobar[X]] {
implicit val m: Converter[X]
}
让伴随对象扩展它:
object Foo extends FoobarCompanion[Foo] {
implicit val m = Converter[Foo] { ... }
}
object Bar extends FoobarCompanion[Bar] {
implicit val m = Converter[Bar] { ... }
}
将方法 companion
添加到 Foobar
本身:
abstract class Foobar[F <: Foobar[F]] {
def companion: FoobarCompanion[F]
}
现在,只要通过 companion
.
Foobar
的实例,您就可以访问正确的隐式
您可能想看看 GenericCompanion 周围的代码是如何组织的。
我不确定我是否正确理解了你的问题,但如果我理解了,问题似乎是你试图通过 implicit Manifest[R]
希望编译器能够找到正确的 MapConvert[R]
在伴随对象中,这种方法不起作用,因为 Manifest
没有扩展 implicit
参数的查找范围。但为什么不能将 MapConvert[R]
本身作为 implicit
参数传递给 fromMapToRequest
?当我将签名更改为
def fromMapToRequest[R <: Request[R]](spec: Spec)(implicit mc: MapConvert[R]): R = {
代码按我预期的方式编译和工作(在线查看 here)。如果您需要 Manifest[R]
进行其他检查,只需传递两个 implicit
参数即可。