2

Scala 2.10-RC5 を使用しています

これが私のコードです:

object Fbound {
    abstract class E[A <: E[A]] {
        self: A =>
        def move(a: A): Int
    }
    class A extends E[A] {
        override def toString = "A"
        def move(a: A) = 1
    }
    class B extends E[B] {
        override def toString = "B"
        def move(b: B) = 2
    }
    def main(args: Array[String]): Unit = {
        val a = new A
        val b = new B
        val l = List(a, b)
        val t = l.map(item => item.move(null.asInstanceOf[Nothing]))
        println(t)
    }
}

プログラムを実行すると、例外が発生します。

Exception in thread "main" java.lang.NullPointerException
    at fb.Fbound$$anonfun$1.apply(Fbound.scala:20)
    at fb.Fbound$$anonfun$1.apply(Fbound.scala:20)
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
    at scala.collection.TraversableLike$$anonfun$map$1.apply(TraversableLike.scala:244)
    at scala.collection.immutable.List.foreach(List.scala:309)
    at scala.collection.TraversableLike$class.map(TraversableLike.scala:244)
    at scala.collection.AbstractTraversable.map(Traversable.scala:105)
    at fb.Fbound$.main(Fbound.scala:20)
    at fb.Fbound.main(Fbound.scala)

私の質問は:

  1. コンパイラを通過しても実行時に失敗するのはなぜですか?
  2. 一番下に行を追加するval t1 = l.map(item => item.move(item))と、コンパイラが失敗します。なぜですか?
4

2 に答える 2

3

を使用したコードは、 がすべてのサブクラスであるため、 に必要な型に準拠しているnull.asInstanceOf[Nothing]ため、コンパイルされます。言うまでもなく、実行時に例外がスローされます。Nothingmove

指定した 2 行目をコンパイルしようとすると、このエラーの行に何かが表示されます。

<console>:19: error: type mismatch;
 found   : E[_6(in value $anonfun)] where type _6(in value $anonfun) >: B with A
 <: E[_ >: B with A <: Object]
 required: <root>._6

2 つのインスタンスを同じListに配置すると、それらの要素のタイプに関する重要な情報が失われます。コンパイラは、型の要素を同じ型のオブジェクトのメソッドにT >: B with A <: E[_ >: B with A]渡すことができることを保証moveできません。これは、あなたができないのと同じ方法です。

val c: E[_ >: B with A] = new A
val d: E[_ >: B with A] = new B
c.move(d) // note: the _ in c and d declarations are different types!

この説明を完全に確信するには、自己型について十分に知っているわけではありませんが、インスタンス レベルの制限ではなく、クラス レベルの制限であるように思えます。つまり、 で型パラメーターに関する情報を失うと、コンパイラーが引数の特定Eの型について知ることを期待できなくなります。move

インスタンス レベルの制限については、this.type. 次のように定義した場合move:

def move(a: this.type): Int

あなたのコードはコンパイルされますが、それを呼び出しているのと同じインスタンスmoveしか受け入れないため、それはあなたが望むものではないと思います。これは役に立ちません。

あなたが望む方法でその制限を強制する方法は考えられません。私が知る限り、ある程度のインスタンスへのバインドがある型変数 (つまりtype T = A、 classで型変数を定義する) を使用してそれを行うことをお勧めします。Eおそらく、あなたの特定の状況をより詳細に説明できますか?

于 2012-12-30T20:50:42.147 に答える
2

Re: 二つ目の質問

問題はメソッドの引数のタイプですmove。代わりに、何らかの方法でサブクラスに関連付けられている場合、それを機能させることが可能であることがわかりません。

  • lE-sのリストの「何らかの形式」である必要があります
  • したがって、item「何らかの形」になりますE
  • への引数の型moveは同じ型でなければなりません

これが、型引数で機能させる唯一の方法です(クラスAの名前と混同しないように、型引数の名前をXに変更し、自己型宣言を削除します。これは、この問題/議論には関係ないと思います):

abstract class E[X <: E[X]]
{
    def move (a: E[_]): Int
}

class A extends E[A]
{
    def move(a: E[_]): Int = 1
}
class B extends E[B]
{
    def move(b: E[_]): Int = 2
}

...
{
    val a = new A
    val b = new B
    val l = List (a, b)
    val t = l.map (item => item.move(item))
    ...
}

誰かがより「型を制限する」解決策を提供できれば、私は本当にそれを見たいと思っています。


Rui が提案したように、別のオプションは、次のような型メンバーを使用することです。

abstract class E
{
    type C <: E
    def move (x: E): Int
}

class A extends E
{
    type C = A
    def move(x: E): Int = 1
}

class B extends E
{
    type C = B
    def move(x: E): Int = 2
}

...
{
    val a = new A
    val b = new B
    val l = List (a, b)
    val t = l.map (item => item.move(item))
    ...
}
于 2012-12-30T22:23:18.170 に答える