我正在開發一個ORM風格的庫在ReactiveMongo之上。目前我試圖實現一個嵌套的文檔表示 - 但是我一直堅持Scala的類型推理爲我的課程。我對Scala相當陌生,所以每一個幫助都是值得歡迎的。Scala類型推斷限制
這是我的嘗試:
trait MongoDocument[T <: MongoDocument[T]] {
self: T =>
val db: DefaultDB
val collection: String
var fields: List[MongoField[T,_]] = List.empty
def apply(doc: TraversableBSONDocument): T = { // loads the content of supplied document
fields.foreach { field =>
doc.get(field.field).foreach(field.load(_))
}
this
}
}
trait MongoMetaDocument[T <: MongoDocument[T]] extends MongoDocument[T] {
self: T =>
type DocType = T
protected val clazz = this.getClass.getSuperclass
def create: T = clazz.newInstance().asInstanceOf[T]
def find(id: String): Future[Option[T]] = {
db(collection).find(BSONDocument("_id" -> BSONObjectID(id)))
.headOption().map(a => a.map(create.apply(_)))
}
}
abstract class MongoField[Doc <: MongoDocument[Doc], T](doc: MongoDocument[Doc], val field: String) {
var value: Option[T] = None
def is: Option[T] = value
def apply(in: T) { value = Some(in) }
def unset() { value = None }
def load(bson: BSONValue)
doc.fields ::= this
}
class MongoInteger[Doc <: MongoDocument[Doc]](doc: Doc, field: String)
extends MongoField[Doc, Int](doc, field) {
def load(bson: BSONValue) { value = Try(bson.asInstanceOf[BSONNumberLike].toInt).toOption }
}
class MongoDoc[Doc <: MongoDocument[Doc], SubDocF <: MongoMetaDocument[SubDoc], SubDoc <: MongoDocument[SubDoc]](doc: Doc, field: String, meta: SubDocF)
extends MongoField[Doc, SubDocF#DocType](doc, field) {
def load(bson: BSONValue) {
value = Try(bson.asInstanceOf[TraversableBSONDocument]).toOption.map { doc =>
meta.create.apply(doc)
}
}
}
假設我有以下代碼:
class SubEntity extends MongoDocument[SubEntity] {
val db = Db.get
val collection = ""
val field = new MongoInteger(this, "field")
}
object SubEntity extends SubEntity with MongoMetaDocument[SubEntity]
我還想寫另一個實體爲:
class Another extends MongoDocument[Another] {
val db = Db.get
val collection = "test"
val subEntity = new MongoDoc(this, "subEntity", SubEntity)
}
object Another extends Another with MongoMetaDocument[Another]
凡Db.get
只是返回一個DefaultDB
。
但是斯卡拉不能推斷該類型的MongoDoc
例如,即使我想他們可能是inferrable(如Doc
很容易推斷出,SubDocF
可以推斷SubEntity.type
和SubEntity
混入只是MongoMetaDocument[SubEntity]
那麼SubDoc
類型必須是SubEntity
) 。如果我使用下面的代碼,一切都很好:
val subEntity = new MongoDoc[Another,SubEntity.type,SubEntity](this, "subEntity", SubEntity)
有沒有一些解決方案,類型不需要明確設置?由於我需要構建一個擴展自MongoDocument
特徵的類的實例,我試圖通過使用具有create
方法的元對象來解決這個問題。
目前我唯一提出的解決方法是使用implicitly
,但這會使實體定義更加噁心。
謝謝你幫我理清了這一點(或者給我一些提示如何安排我的類層次結構,這將不會是一個問題)
我試圖根據你的回答來重寫我的實現,我已經在不同的問題上運行。對'MongoDocument#DocType'的類型沒有限制,因此庫的用戶可以用'DocType = SubEntity'指定實體'Another'(導致至少在運行時出現運行時異常)。而且'MongoDocument.apply'方法試圖可鏈接並返回,因此'this'不能編譯爲'this',只是'MongoDocument'類型。 (以及「this'不是'MongoDocument#Doctype'的實例的缺失假設也會在不同的地方破壞我的代碼) – 2013-03-12 09:02:37
我已更改我的問題中的代碼,以便更明顯地發現問題出現的位置。 (並且感謝您的回答!) – 2013-03-12 09:28:01
DocType存在與您的示例中相同的限制。它必須是MongoDocument的子類型。在你的情況下,你認爲在這種情況下,你知道這個提示是另一個必須被指定爲Another擴展MongoDocument [Another],但是另一個擴展MongoDocument [SubEntity]仍然允許在你的例子中,是的,無論如何,我們允許人們編寫惡意類,但是沒有一種好的方法來防止這種類,並且仍然以類型安全的方式獲得您希望的返回類型多態。 – stew 2013-03-12 13:14:03