1

アイテムを取得する Accessor クラスがあります。また、Item をパラメーターとして取り、その Item の最新バージョンをデータベースから返すこともできます。Item を作成するとき、自分自身をパラメーターとして Item に渡します。

アクセサー インスタンスがそれ自体で作成されたアイテムのみを受け入れることを静的に要求するようコンパイラーに求めます。これは、Scala の singleton-object 型の使用方法でカバーされていましたか? ただし、Item インスタンスがそれ自体をパラメーターとして独自のアクセサーに渡して、それ自体の最新バージョンを取得できるようにすることも必要です。

これの難しさは、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

型パラメーターがシングルトン型でなければならないことを指定する方法があれば。たとえば、次のように言うことができます (I'm inventing notation here)

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 に答える