6

次のステートメントは正常にコンパイルされ、期待どおりに機能します。

val map : Map[_ >: Int with String, Int] = Map(1 -> 2, "Hello" -> 3)

ただし、マップに追加しようとすると、次のようになります。

map + ((3,4))

また

map + (("Bye", 4))

次に、タイプの不一致が発生します。

見つかった:java.lang.String( "Bye")

必須:_ $ 1ここで、タイプ_ $ 1>:文字列を含むInt

Anyキーのタイプとして許可するように型署名を弱めると、これはすべて期待どおりに機能します。

私の直感によると、これはMapのキータイプの非分散性に関係しており、_ $ 1はの特定のスーパータイプとして何らかの形で修正されていInt with Stringますが、これには特に満足していません。誰かが何が起こっているのか説明できますか?

追加するために編集:

これがどこで発生するのか疑問に思っている場合は、次のようなことをした場合に取得する署名です。

val map = if (true) Map(1 -> 2) else Map("1" -> 2)
4

1 に答える 1

9

あなたは誤解してInt with Stringいます。これはIntとStringの結合ではなく、交差点であり、IntとStringの場合は空です。文字列である値のセットとIntである値のセットではなく、文字列の特性を持つIntの特性を持つ値のセットでもあります。そのような値はありません。

を使用しEither[Int, String]て、を使用できますMap[Left(1) -> 2, Right("Hello") -> 3)。どちらも正確には共用体ではなく、AU Bではなく、識別された共用体A + Bです。Either[Int、Int]はIntと同じではないという違いを理解できます。実際、これは(Int、Boolean)と同型です。Intがあり、どちら側でもあることがわかります。AとBが互いに素である場合(IntとStringがそうであるように)、A+BとAUBは同型です。

または(気の弱い人向けではありません)、MilesSabinによる共用体型の可能なエンコーディングを見ることができます。(既存のクラスMapで実際に使用できるかどうかはわかりませんが、試してみるよりも確実ではありませんが、それでも最も興味深い読み物になります)。


編集:質問とコードを読むのが速すぎます、ごめんなさい

下限Int with Stringはと同じNothingであるためMap[_ >: Int with String, Int]、はと同じでMap[_ >: Nothing, Int]あり、Nothing下限が暗示されているため、これはMap[_, Int]です。あなたの実際MapMap[Any, Int]です。Int with Stringにもかかわらず、ブールキーを追加することもできます。val宣言が機能するようにAMap[Any, Int]を入力できます。Map[_, Int]ただし、入力すると、キーのタイプに関するすべての情報が失われます。キーのタイプがわからないため、テーブルから何も追加(取得)できません。

可能なキーがないため、UpperBoundはこれ以上良くありませんでした。最初のval宣言でさえ失敗します。


編集2:に関してif (true) Map(1 -> 2) else Map("1" -> 2)

これはと同じではありませんMap(1 -> 2, "1" -> 2)。とのより一般的なスーパータイプと同様Map[Any, Int]に、それはより単純で、単純にです。AnyIntString

一方、Map(1 -> 2)は、、Map[Int, Int]およびMap["1", 2]ですMap[String, Int]。との共通のスーパータイプを見つけるという問題がありますが、との共通のスーパータイプをMap[Int, Int]見つけることMap[String, Int]はできません。IntString

実験してみましょう。Map2番目のパラメーターは共変です。キーではなく値としてIntandを使用する場合:String

if (true) Map(1 -> 2) else Map(1 -> "2")
res1: scala.collection.immutable.Map[Int, Any]

共分散を使用すると、すべての型パラメーターに共通のスーパータイプを使用するだけです。

反変型の場合:

class L[-T]
object L{def apply[T](t: T) = new L[T])
class A
class B extends A
class C
if (true) L(new A) else L(new C)
res2: L[A with C]
if (true) L(new A) else L(new B)
res3: L[B]

交差点を取りますA with C.WhenBはのサブタイプでありAAwithBはちょうどBです。

2つのタイプが関連している場合、Mapの非バリアントパラメータを使用するようになりました

if (true) Map(new A -> 1) else Map(new B -> 1)
res4: scala.collection.immutable.Map[_ >: B <: A, Int]

そのようなタイプは役に立たないわけではありません。タイプのキーを使用して値にアクセスしたり、値を追加したりできますB。ただし、キーの値にアクセスすることはできませんA。これは実際のマップにあるものなので(のでtrue)、頑張ってください。にアクセスするkeySetと、と入力されSet[A]ます。キーの種類に関する情報が不完全であり、実行できることは限られていますが、マップの種類に関する知識が限られていることを考えると、これは必要な制限です。を使用Int and Stringすると、最小限の情報が得られ、下限Anyと上限はに相当しNothingます。Nothingの上限により、キーをパラメーターとして受け取るルーチンを呼び出すことができなくなります。keySetタイプが下限Set[Any]である、を引き続き取得できます。Any

于 2012-01-06T11:42:30.773 に答える