8

以下を考慮してください (Scala 2.8.1 および 2.9.0 でテスト済み):

trait Animal
class Dog extends Animal

case class AnimalsList[A <: Animal](list:List[A] = List())
case class AnimalsMap[A <: Animal](map:Map[String,A] = Map())

val dogList = AnimalsList[Dog]()  // Compiles
val dogMap = AnimalsMap[Dog]()    // Does not compile

最後の行は次のように失敗します。

error: type mismatch;
 found   : scala.collection.immutable.Map[Nothing,Nothing]
 required: Map[String,Main.Dog]
Note: Nothing <: String, but trait Map is invariant in type A.
You may wish to investigate a wildcard type such as `_ <: String`. (SLS 3.2.10)
Error occurred in an application involving default arguments.
    val dogMap = AnimalsMap[Dog]()    // Does not compile
                       ^
one error found

これを変更するとval dogMap = AnimalsMap[Dog](Map())修正されますが、デフォルトの引数値を利用できなくなります。

リストの対応物が期待どおりに機能するのに、デフォルト値が Map[Nothing,Nothing] と推論されるのはなぜですか? 引数のデフォルト値を使用する AnimalsMap インスタンスを作成する方法はありますmapか?


編集:私は、より差し迫った 2 番目の質問への回答を受け入れましたが、キーの型Map()がこれら 2 つのケースで異なって推論される理由を知りたいと思います。

case class AnimalsMap1(map:Map[String,Animal] = Map())
val dogs1 = AnimalsMap1() // Compiles

case class AnimalsMap2[A <: Animal](map:Map[String,A] = Map())
val dogs2 = AnimalsMap2[Dog]() // Does not compile

編集2:型の境界は無関係であるようです-ケースクラスへのパラメトリック型は問題を引き起こします:

case class Map3[A](map:Map[String,A] = Map())
val dogs3 = Map3[Dog]() // Does not compile
4

2 に答える 2

21

Scala には、ジェネリック パラメータでクラスを共変/反変として定義できる機能があります。

共分散の例として、 if class Student extends PersonthenList[Student]が「伸びる」と考えるのが自然List[Person]です。これは、 を受け入れるすべてのメソッドがList[Person]object を問題なく操作できるためList[Student]です。これは Java では不可能です (メソッドもジェネリックにしないと)。

反変性はその反対であり、説明が少し難しいです。タイプがジェネリッククラスにプッシュされ、読み取られない場合に必要です(List[Person]リストの要素を読み取る場合)。一般的な例は関数です。関数への引数の型はそれに入れられるので、メソッドが関数を期待している場合、関数で呼び出すPerson => StringことはできませんStudent => String(人で引数を呼び出しますが、学生を期待します)。

Scala ではNothing、すべてを暗黙的に拡張することも定義されています。ボトムタイプです。したがって、常にX. に対してList[Nothing]「拡張」します。作成し、共分散が.List[X]List()List[Nothing]val x: List[Person] = List()

とにかく、 Map はキーの型が不変です。その理由は、 aMap[A, B]は関数に似ているA => Bため、 でのみ反変になるためAです。Map[Student, String]もう 1 つの方法は、 a を期待するメソッドに渡すとどうなるかを考えることですMap[Person, String]。明らかに、Personオブジェクトをそこに配置しようとする可能性がありますが、これは良くありませんが、他の方法は問題ありません。一方、Map は と見なすことができますIterable[(A, B)]。ここでは、A で共変でなければなりません。したがって、その値は不変です。

Map[Nothing, Nothing]その結果、 aを type の変数に割り当てることはできませんMap[String, Animal]Map()を作成しますMap[Nothing, Nothing]

コンパイラは次のように伝えます。

scala> val dogs3 = Map3[Dog]()
<console>:13: error: type mismatch;
 found   : scala.collection.immutable.Map[Nothing,Nothing]
 required: Map[String,Dog]
Note: Nothing <: String, but trait Map is invariant in type A.
You may wish to investigate a wildcard type such as `_ <: String`. (SLS 3.2.10)
Error occurred in an application involving default arguments.
       val dogs3 = Map3[Dog]()
                       ^
于 2011-05-26T09:37:59.363 に答える
4

コンパイラに少し助けを与えるだけです:

case class AnimalsMap[A <: Animal](map:Map[String,A] = Map[String, A]())
                                                          ^^^^^^^^^^^

あなたのソリューションが機能しない理由の詳細は、Scala の型推論に詳しい人に任せます…</p>

編集:この動作のわかりやすい説明については、IttayD の回答を参照してください。

于 2011-05-26T08:37:09.633 に答える