この問題は、Classoverの使用とは関係なく、単なる値 ( 、andなど) に対する照合よりTagも case オブジェクト ( IntegerTagand など) に対する照合にすべて関係しています。StringTagTagOfIntegerClassOfIntegerClassOfString
最初の例の 4 つのバリアントをコンパイルしてみましょう。
バージョン 1:
class Tag[T]
case object IntegerTag extends Tag[Int]
case object StringTag extends Tag[String]
def defaultValue[T](typ: Tag[T]): T = typ match {
case IntegerTag => 0
case StringTag => ""
}
バージョン 2:
class Tag[T]
case class IntegerTag() extends Tag[Int]
case class StringTag() extends Tag[String]
def defaultValue[T](typ: Tag[T]): T = typ match {
case IntegerTag() => 0
case StringTag() => ""
}
バージョン 3:
class Tag[T]
class IntegerTag extends Tag[Int]
class StringTag extends Tag[String]
def defaultValue[T](typ: Tag[T]): T = typ match {
case _: IntegerTag => 0
case _: StringTag => ""
}
バージョン 4:
class Tag[T]
val IntegerTag: Tag[Int] = new Tag[Int]
val StringTag: Tag[String] = new Tag[String]
def defaultValue[T](typ: Tag[T]): T = typ match {
case IntegerTag => 0 // error: type mismatch
case StringTag => "" // error: type mismatch
}
それらをコンパイルしようとすると、バージョン 1、2、および 3 は正常にコンパイルされますが、バージョン 4 はコンパイルされないことがわかります。その理由は、バージョン 1、2、および 3 では、パターン マッチングにより、コンパイラがどの型が であるかを確実に認識できるようになるためですT。
バージョン 1 ではcase IntegerTag =>. はケース オブジェクトであるため、(それ自体を除いて)IntegerTag と等しいインスタンスが存在できないことが確実にわかっています。したがって、ここに一致するものがある場合、 の実行時型はのみとなり、これは を拡張します。したがって、安全にそれを推測できます。IntegerTagIntegerTagIntegerTagIntegerTagTag[Int]T = Int
バージョン 2 ではcase IntegerTag() =>. これはケース クラスであり、 が を拡張する のインスタンスであるIntegerTag場合にのみ一致する可能性があることがわかっています。したがって、安全にそれを推測できます。typIntegerTagTag[Int]T = Int
バージョン 3 ではcase _: IntegerTag =>. つまり、型に対して明示的に一致しIntegerTagます。繰り返しになりますが、これは extendstyp型であることがわかり、安全に推測できます。IntegerTagTag[Int]T = Int
さて、バージョン 4 の問題は、 の実行時の型について保証がないことですtyp。これは、このバージョンでは だけを行うためです。case IntegerTag =>ここIntegerTagで、 はval. つまり、 の場合にのみ一致しtyp == IntegerTagます。問題は、typが等しいIntegerTag(つまり、typ.==(IntegerTag)true を返す) という事実が、 の実行時の型について何も教えてくれないことですtyp。実際、関係のないクラスのインスタンスと等しくなるように (または単に同じジェネリック クラスのインスタンスと等しくなるが型引数が異なる)、同等性を再定義することができます。例として、次のことを考慮してください。
val StringTag: Tag[String] = new Tag[String]
val IntegerTag: Tag[Int] = new Tag[Int] {
override def equals( obj: Any ) = {
(obj.asInstanceOf[AnyRef] eq this) || (obj.asInstanceOf[AnyRef] eq StringTag)
}
}
println(StringTag == StringTag) // prints true
println(StringTag == IntegerTag) // prints false
println(IntegerTag == IntegerTag) // prints true
println(IntegerTag == StringTag) // prints true
IntegerTag == StringTagは true を返します。つまり、StringTagメソッド に渡した場合、 actual がではなくのインスタンスであっても、defaultValueと一致することを意味します。これは、 に一致する があるという事実が、 の実行時の型に関して何も伝えていないことを示しています。そのため、コンパイラは の正確な型について何も想定できません。宣言された静的型からのみ、それが であることがわかりますが、まだ不明です。case IntegerTag =>StringTagTag[String]Tag[Int]case IntegerTag =>typtypTag[T]T