我有一个Accessor类来检索Items。它还可以将Item作为参数,并从数据库返回该项的最新版本。当它创建一个Item时,它将自身作为参数传递给Item。
我希望编译器静态地要求Accessor实例只接受自己创建的Items。这由How to use Scala's singleton-object types?涵盖,但是我还希望Item实例能够将自身作为参数传递给自己的Accessor以检索其自身的最新版本。
这样做的难点在于Item类定义中的类型参数,如此
class Item[A <: Accessor](acc: A)
不能指代acc本身的类型。从项目acc.type <: A <: Accessor
的角度来看,项目中的this
是Item[A]
,而不是Item[acc.type]
。因此,这不起作用:
class Item[A <: Accessor](acc: A) {
acc.accept(this) // Type Mismatch: found Item[A], required Item[Item.this.acc.type]
}
class Accessor {
def make() = new Item[this.type](this)
def accept(item: Item[this.type]) = "accepted"
}
然后我尝试了这个:
object A1 extends Accessor[A1.type](A1) // illegal cyclic reference involving object A1
class Item[+A <: Accessor[A]](acc: A) {
acc.accept(this)
A1.accept(this) // Compile error (good)
}
class Accessor[+A <: Accessor[A]](me: => A) {
def make = new Item[A](me)
def accept(item: Item[A]) = "accepted"
}
问题实际上是创建Accessor的实例。
我尝试了上面的一个变体,结果证明是同一个基本困境的一个更加混乱的化身:
object A1 extends Accessor {
type A = A1.type
def me = A1
}
class Item[+AA <: Accessor](acc: AA {type A = AA}) {
acc.accept(this)
A1.accept(this) // Compile error (good)
}
class Accessor {
type A <: Accessor
def me: A // can't do {type A = A} because its a cyclic error again
def make = new Item[A](me) // Type Mismatch: found this.A, required this.A {type A = Accessor.this.A}
def accept(item: Item[A]) = "accepted"
}
最后,我尝试使A
类型参数逆变,以便Item [A]是Item [acc.type]的子类型,并且将被acc接受。
val a1 = new Accessor
val a2 = new Accessor
val item1 = a1.make
val item2 = a2.make
val itemA = new Item[Accessor](a2)
val item12 = new Item[A1.type](a2) // compile error (good)
a1.accept(itemA) // no compile error (bad), but I can prevent creation of Item[Accessor]s
a1.accept(item2) // compile error (good)
class Item[-A <: Accessor](acc: A) {
acc.accept(this)
val acc2 = new Accessor
acc2.accept(this) // compile error (good)
// here Item[Accessor] <: Item[A] <: Item[acc.type]
// and Item[Accessor] <: Item[acc2.type]
// but Item[A] is not necessarily <: Item[acc2.type]
}
class Accessor {
def make() = new Item[this.type](this)
def accept(item: Item[this.type]) = "accepted"
}
这最接近于我尝试过的任何工作。唯一的问题是它填满了我的对象层次结构,因为我不能这样做:
class ImmutableAccessor extends Accessor
class ImmutableItem[-A <: ImmutableAccessor](acc: A) extends Item[A] // fails due to contravariance in A
如果只有某种方式指定类型参数必须是单例类型。例如,你可以说(我在这里发明了符号)
class Item[A:type <: Accessor](acc: A)
然后A
将成为acc
的单身类型,我们会笑。
答案 0 :(得分:1)
我们需要的秘诀就是利用&lt;:&lt;在Predef中找到的类,使用隐式值。对于Singleton&#34;你是对的。没有做我们想做的一切。
class Item[-A <: Accessor](acc: A)(implicit sing: A <:< Singleton) {
acc.accept(this)
}
class Accessor {
def make() = new Item[this.type](this)
def accept(item: Item[this.type]) = "accepted"
}
class BetterItem[-B <: BetterAccessor](b: B)(implicit bsing: B <:< Singleton)
extends Item[B](b)(bsing)
}
class BetterAccessor extends Accessor
现在继承更好。我们可以做以下的事情
val a1 = new Accessor
val a2 = new Accessor
val b3 = new BetterAccessor
val i1 = a1.make
val i3 = b3.make
a1.accept(i1) // GOOD
//a2.accept(i1) // compile time error
//b3.accept(i1) // compile time error
//new Item[Accessor](a2) // compile time error
//a1.accept(i3) // compile time error
b3.accept(i3) // GOOD
我们将编译时错误准确地发送到我们想要的地方。
答案 1 :(得分:0)
我没有关于处理继承的完整解决方案,但我可以建议一个小的改动以防止像
这样的行a1.accept(itemA)
来自编译。在Item的定义中包含一个明确的“with Singleton”。
class Item[-A <: Accessor with Singleton](acc: A) {
然后尝试创建一个新的“Item [Accessor]”将无法编译。