この問題は、Class
overの使用とは関係なく、単なる値 ( 、andなど) に対する照合よりTag
も case オブジェクト ( IntegerTag
and など) に対する照合にすべて関係しています。StringTag
TagOfInteger
ClassOfInteger
ClassOfString
最初の例の 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
と等しいインスタンスが存在できないことが確実にわかっています。したがって、ここに一致するものがある場合、 の実行時型はのみとなり、これは を拡張します。したがって、安全にそれを推測できます。IntegerTag
IntegerTag
IntegerTag
IntegerTag
Tag[Int]
T = Int
バージョン 2 ではcase IntegerTag() =>
. これはケース クラスであり、 が を拡張する のインスタンスであるIntegerTag
場合にのみ一致する可能性があることがわかっています。したがって、安全にそれを推測できます。typ
IntegerTag
Tag[Int]
T = Int
バージョン 3 ではcase _: IntegerTag =>
. つまり、型に対して明示的に一致しIntegerTag
ます。繰り返しになりますが、これは extendstyp
型であることがわかり、安全に推測できます。IntegerTag
Tag[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 =>
StringTag
Tag[String]
Tag[Int]
case IntegerTag =>
typ
typ
Tag[T]
T