0

我是 scala 的新手,无法理解 Lift 家伙是如何实现 Record API 的。然而,问题不在于这个 API,而更多的是关于 Scala。我对 Lift 中使用的类模式中的对象如何工作感兴趣。

class MainDoc private() extends MongoRecord[MainDoc] with ObjectIdPk[MainDoc] {
  def meta = MainDoc

  object name extends StringField(this, 12)
  object cnt extends IntField(this)
}

object MainDoc extends MainDoc with MongoMetaRecord[MainDoc]

在上面的代码片段中,您可以看到在 Lift 中如何定义记录。有趣的部分是字段被定义为对象。API 允许您像这样创建实例:

val md1 = MainDoc.createRecord
  .name("md1")
  .cnt(5)
  .save

这可能是通过使用 apply 方法完成的?但与此同时,您可以通过执行以下操作来获取值:

val name = md1.name

这一切如何运作?在类的范围内,对象是否不是静态的。或者它们只是一些内部表示的构造函数类?怎么可能遍历所有字段,你使用反射吗?

谢谢,奥托

4

2 回答 2

3

奥托,

你在正确的轨道上多或少。您实际上不需要将字段定义为对象,您可以将示例编写为

class MainDoc private() extends MongoRecord[MainDoc] with ObjectIdPk[MainDoc] {
  def meta = MainDoc

  val name = new StringField(this, 12)
  val cnt= new IntField(this)
}

object MainDoc extends MainDoc with MongoMetaRecord[MainDoc]

net.liftweb.record.Field trait 确实包含一个相当于 set 的 apply 方法。这就是为什么您可以在实例化对象后按名称分配字段的原因。

您提到的字段参考:

val name = md1.name

将名称键入为 StringField。如果你想的是

val name: String = md1.name

这将无法编译(除非在范围内隐含转换 Field[T] => T)。检索字段的字符串值的正确方法是

val name = md1.name.get

Record 确实使用反射来收集字段。当您在类中定义对象时,编译器将创建一个字段来保存对象实例。从反射的角度来看,对象看起来与我之前提到的定义字段的替代方式非常相似。每个定义都可能创建字段类型的子类,但这与

val name = new StringField(this, 12) {
  override def label: NodeSeq = <span>My String Field</span>
}
于 2012-06-05T18:20:40.577 回答
1

你是对的,它是apply方法。Record 的Field基类定义了一些apply方法。

def apply(in: Box[MyType]): OwnerType
def apply(in: MyType): OwnerType

通过返回OwnerType,您可以将调用链接在一起。

关于object定义字段的使用,一开始我也很困惑。object标识符定义了特定范围内的对象。尽管将其视为单例模式object的快捷方式很方便,但它比这更灵活。根据Scala 语言规范(第 5.4 节):

它大致等价于以下惰性值的定义:
lazy val m = new sc with mt1 with ... with mtn { this: m.type => stats }

<snip/>

上面给出的扩展对于顶级对象是不准确的。这不可能是因为变量和方法定义不能出现在包对象之外的顶层(第 9.3 节)。相反,顶级对象被转换为静态字段。

关于遍历所有字段,Record对象定义了一个allFields返回 a 的方法List[net.liftweb.record.Field[_, MyType]]

于 2012-06-05T18:16:51.893 回答