1

我有一个检索项目的访问器类。它还可以将 Item 作为参数并从数据库中返回该 Item 的最新版本。当它创建一个 Item 时,它会将自己作为参数传递给 Item。

我希望编译器静态地要求 Accessor 实例只接受它自己创建的项目。如何使用 Scala 的单例对象类型?但是我也希望一个 Item 实例能够将自己作为参数传递给它自己的 Accessor 以检索其自身的最新版本。

这样做的困难在于 Item 类定义中的类型参数像这样

class Item[A <: Accessor](acc: A)

不能引用 acc 本身的类型。从 Item 的角度来看,acc.type <: A <: AccessorthisItem 中,它是一个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"
}

问题实际上是创建访问器的实例。

我尝试了上面的一个变体,结果证明是同样基本困境的一个更混乱的化身:

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我们会笑。

4

2 回答 2

1

我们这里需要的秘诀是利用 Predef 中的 <:< 类,使用隐式值。你说得对,“with Singleton”并没有做我们想要的一切。

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

我们会在我们想要的地方得到编译时错误。

于 2012-10-21T15:28:37.343 回答
0

我没有关于处理继承的完整解决方案,但我可以建议进行一些小的更改以防止像

a1.accept(itemA)

从编译。在 Item 的定义中包含一个显式的“with Singleton”。

class Item[-A <: Accessor with Singleton](acc: A) {

然后尝试创建一个新的“Item[Accessor]”将无法编译。

于 2012-10-17T23:11:19.660 回答