如何创建类型化工厂方法构造函数的类层次结构并使用抽象类型从 Scala 访问它们?

Posted

技术标签:

【中文标题】如何创建类型化工厂方法构造函数的类层次结构并使用抽象类型从 Scala 访问它们?【英文标题】:How do I create a class hierarchy of typed factory method constructors and access them from Scala using abstract types? 【发布时间】:2011-11-17 05:00:44 【问题描述】:

(基本上我需要对这两个问题(1,2)进行某种综合,但我自己不够聪明,无法将它们结合起来。)

我在 Scala 中有一组这样的 JAXB 表示:

abstract class Representation 
  def marshalToXml(): String = 
    val context = JAXBContext.newInstance(this.getClass())
    val writer = new StringWriter
    context.createMarshaller.marshal(this, writer)
    writer.toString()
  


class Order extends Representation 
  @BeanProperty
  var name: String = _
  ...

class Invoice extends Representation  ... 

我遇到的问题是我的解组“构造函数”方法:

def unmarshalFromJson(marshalledData: String): My Representation Subclass = 
  val mapper = new ObjectMapper()
  mapper.getDeserializationConfig().withAnnotationIntrospector(new JaxbAnnotationIntrospector())
  mapper.readValue(marshalledData, this.getClass())


def unmarshalFromXml(marshalledData: String): My Representation Subclass = 
  val context = JAXBContext.newInstance(this.getClass())
  val representation = context.createUnmarshaller().unmarshal(
    new StringReader(marshalledData)
  ).asInstanceOf[Type of My Representation Subclass]
  representation // Return the representation

具体来说,我不知道如何以类型安全和 DRY 的方式将这些解组方法附加到我的每个类,然后从 Scala 调用它们(希望有时只使用抽象类型信息)。换句话说,我想这样做:

val newOrder = Order.unmarshalFromJson(someJson)

更雄心勃勃:

class Resource[R <: Representation] 
    getRepresentation(marshalledData: String): R = 
        R's Singleton.unmarshalFromXml(marshalledData)

就我的特殊绊脚石而言:

我不知道是否应该在 Representation 类中或在单例 Representation 对象中定义我的 unmarshalFrom*() 构造函数 - 如果是后者,我不知道如何自动继承它向下遍历OrderInvoice 等的类层次结构。 我无法让 this.type (as per this answer) 作为一种自键入 unmarshalFromJson() 的方式工作 - 我在调用 readValue() 时收到编译错误 type mismatch; found: ?0 where type ?0 required: Representation.this.type 我不知道如何使用隐式 Default[A] 模式 (as per this answer) 来处理我的 Representation 类层次结构,以便仅使用类型信息调用单例解组构造函数

我知道这是一个涉及各种不同(但相关)问题的庞大问题 - 任何帮助都非常感激!

亚历克斯

【问题讨论】:

【参考方案1】:

关键是不要尝试将方法附加到类,而是将其作为参数传递。为了指示您期望的类型并让类型系统处理将其传入。我尝试使解组调用看起来有点像 DSL。

val order = UnMarshalXml( xml ).toRepresentation[Order]

以下是完全可测试的代码sn-p

abstract class Representation 
  def marshalToXml(): String = 
      val context = JAXBContext.newInstance(this.getClass)
      val writer = new StringWriter
      context.createMarshaller.marshal(this, writer)
      writer.toString
  


@XmlRootElement
class Order extends Representation 
  @BeanProperty
  var name: String = _


case class UnMarshalXml( xml: String ) 
  def toRepresentation[T <: Representation](implicit m:Manifest[T]): T = 
    JAXBContext.newInstance(m.erasure).createUnmarshaller().unmarshal(
      new StringReader(xml)
    ).asInstanceOf[T]
  


object test 
  def main( args: Array[String] ) 
    val order = new Order
    order.name = "my order"
    val xml = order.marshalToXml()
    println("marshalled: " + xml )
    val received = UnMarshalXml( xml ).toRepresentation[Order]
    println("received order named: " + received.getName )
  

如果您运行 test.main,您应该会看到以下输出

marshalled: <?xml version="1.0" encoding="UTF-8" standalone="yes"?><order><name>my order</name></order>
received name: my order

【讨论】:

谢谢 Neil - 我真的很喜欢你用案例类代替我笨拙的单例想法的方法。您的示例运行良好 - 但不幸的是我无法让它与我的第二个(更困难的)用例一起工作:class Resource[R &lt;: Representation] getRepresentation(marshalledData: String): R = UnMarshalXml(xml).toRepresentation[R] 。运行它会给我错误:No Manifest available for R。如果您有任何进一步的想法,请告诉我! 嗨,尼尔 - 我想通了。在下面发布它作为另一个答案。【参考方案2】:

这是我用来支持第二个用例和第一个用例的 Neil 代码的更新版本:

case class UnmarshalXml(xml: String) 

  def toRepresentation[T <: Representation](implicit m: Manifest[T]): T =
    toRepresentation[T](m.erasure.asInstanceOf[Class[T]])

  def toRepresentation[T <: Representation](typeT: Class[T]): T =
    JAXBContext.newInstance(typeT).createUnmarshaller().unmarshal(
      new StringReader(xml)
    ).asInstanceOf[T]

这支持像这样的简单示例:

val order = UnmarshalXml(xml).toRepresentation[Order]

但对于基于抽象类型的用法,您可以这样使用:

val order = UnmarshalXml(xml).toRepresentation[T](typeOfT)

(您在声明 T 时使用另一个 implicit Manifest 抓取并存储了 typeOfT。)

【讨论】:

以上是关于如何创建类型化工厂方法构造函数的类层次结构并使用抽象类型从 Scala 访问它们?的主要内容,如果未能解决你的问题,请参考以下文章

C++ 静态工厂构造函数

依赖注入方法

在工厂函数之外公开本地 TypeScript 类的类型

c++ 当我创建结构数组时,如何使用结构数组内的类的参数调用构造函数?

案例分析:设计模式与代码的结构特性

简单工厂模式