A TypeTag
memecahkan masalah bahwa tipe Scala dihapus pada saat runtime (type erasure). Jika kita ingin melakukannya
class Foo
class Bar extends Foo
def meth[A](xs: List[A]) = xs match {
case _: List[String] => "list of strings"
case _: List[Foo] => "list of foos"
}
kami akan mendapat peringatan:
<console>:23: warning: non-variable type argument String in type pattern List[String]↩
is unchecked since it is eliminated by erasure
case _: List[String] => "list of strings"
^
<console>:24: warning: non-variable type argument Foo in type pattern List[Foo]↩
is unchecked since it is eliminated by erasure
case _: List[Foo] => "list of foos"
^
Untuk mengatasi masalah ini, Manifes diperkenalkan ke Scala. Tetapi mereka memiliki masalah karena tidak dapat mewakili banyak tipe yang berguna, seperti tipe path-dependen:
scala> class Foo{class Bar}
defined class Foo
scala> def m(f: Foo)(b: f.Bar)(implicit ev: Manifest[f.Bar]) = ev
warning: there were 2 deprecation warnings; re-run with -deprecation for details
m: (f: Foo)(b: f.Bar)(implicit ev: Manifest[f.Bar])Manifest[f.Bar]
scala> val f1 = new Foo;val b1 = new f1.Bar
f1: Foo = Foo@681e731c
b1: f1.Bar = Foo$Bar@271768ab
scala> val f2 = new Foo;val b2 = new f2.Bar
f2: Foo = Foo@3e50039c
b2: f2.Bar = Foo$Bar@771d16b9
scala> val ev1 = m(f1)(b1)
warning: there were 2 deprecation warnings; re-run with -deprecation for details
ev1: Manifest[f1.Bar] = Foo@681e731c.type#Foo$Bar
scala> val ev2 = m(f2)(b2)
warning: there were 2 deprecation warnings; re-run with -deprecation for details
ev2: Manifest[f2.Bar] = Foo@3e50039c.type#Foo$Bar
scala> ev1 == ev2 // they should be different, thus the result is wrong
res28: Boolean = true
Dengan demikian, mereka digantikan oleh TypeTags , yang keduanya lebih mudah digunakan dan terintegrasi dengan baik ke dalam API Refleksi baru. Dengan mereka, kita dapat memecahkan masalah di atas tentang tipe path-dependent secara elegan:
scala> def m(f: Foo)(b: f.Bar)(implicit ev: TypeTag[f.Bar]) = ev
m: (f: Foo)(b: f.Bar)(implicit ev: reflect.runtime.universe.TypeTag[f.Bar])↩
reflect.runtime.universe.TypeTag[f.Bar]
scala> val ev1 = m(f1)(b1)
ev1: reflect.runtime.universe.TypeTag[f1.Bar] = TypeTag[f1.Bar]
scala> val ev2 = m(f2)(b2)
ev2: reflect.runtime.universe.TypeTag[f2.Bar] = TypeTag[f2.Bar]
scala> ev1 == ev2 // the result is correct, the type tags are different
res30: Boolean = false
scala> ev1.tpe =:= ev2.tpe // this result is correct, too
res31: Boolean = false
Mereka juga mudah digunakan untuk memeriksa parameter tipe:
import scala.reflect.runtime.universe._
def meth[A : TypeTag](xs: List[A]) = typeOf[A] match {
case t if t =:= typeOf[String] => "list of strings"
case t if t <:< typeOf[Foo] => "list of foos"
}
scala> meth(List("string"))
res67: String = list of strings
scala> meth(List(new Bar))
res68: String = list of foos
Pada titik ini, sangat penting untuk memahami penggunaan =:=
(jenis kesetaraan) dan <:<
(hubungan subtipe) untuk pemeriksaan kesetaraan. Jangan pernah menggunakan ==
atau !=
, kecuali jika Anda benar-benar tahu apa yang Anda lakukan:
scala> typeOf[List[java.lang.String]] =:= typeOf[List[Predef.String]]
res71: Boolean = true
scala> typeOf[List[java.lang.String]] == typeOf[List[Predef.String]]
res72: Boolean = false
Yang terakhir memeriksa kesetaraan struktural, yang sering kali bukan apa yang harus dilakukan karena tidak peduli tentang hal-hal seperti awalan (seperti dalam contoh).
A TypeTag
sepenuhnya dihasilkan oleh kompiler, artinya kompiler membuat dan mengisi TypeTag
ketika seseorang memanggil metode yang mengharapkan a TypeTag
. Ada tiga bentuk tag yang berbeda:
ClassTag
pengganti ClassManifest
sedangkan TypeTag
kurang lebih pengganti Manifest
.
Yang pertama memungkinkan untuk sepenuhnya bekerja dengan array generik:
scala> import scala.reflect._
import scala.reflect._
scala> def createArr[A](seq: A*) = Array[A](seq: _*)
<console>:22: error: No ClassTag available for A
def createArr[A](seq: A*) = Array[A](seq: _*)
^
scala> def createArr[A : ClassTag](seq: A*) = Array[A](seq: _*)
createArr: [A](seq: A*)(implicit evidence$1: scala.reflect.ClassTag[A])Array[A]
scala> createArr(1,2,3)
res78: Array[Int] = Array(1, 2, 3)
scala> createArr("a","b","c")
res79: Array[String] = Array(a, b, c)
ClassTag
hanya menyediakan informasi yang diperlukan untuk membuat tipe saat runtime (yang merupakan tipe terhapus):
scala> classTag[Int]
res99: scala.reflect.ClassTag[Int] = ClassTag[int]
scala> classTag[Int].runtimeClass
res100: Class[_] = int
scala> classTag[Int].newArray(3)
res101: Array[Int] = Array(0, 0, 0)
scala> classTag[List[Int]]
res104: scala.reflect.ClassTag[List[Int]] =↩
ClassTag[class scala.collection.immutable.List]
Seperti yang dapat dilihat di atas, mereka tidak peduli tentang penghapusan tipe, oleh karena itu jika seseorang ingin tipe "penuh" TypeTag
harus digunakan:
scala> typeTag[List[Int]]
res105: reflect.runtime.universe.TypeTag[List[Int]] = TypeTag[scala.List[Int]]
scala> typeTag[List[Int]].tpe
res107: reflect.runtime.universe.Type = scala.List[Int]
scala> typeOf[List[Int]]
res108: reflect.runtime.universe.Type = scala.List[Int]
scala> res107 =:= res108
res109: Boolean = true
Seperti yang dapat dilihat, metode tpe
dari TypeTag
hasil dalam penuh Type
, yang sama kita dapatkan ketika typeOf
disebut. Tentu saja, dimungkinkan untuk menggunakan keduanya, ClassTag
dan TypeTag
:
scala> def m[A : ClassTag : TypeTag] = (classTag[A], typeTag[A])
m: [A](implicit evidence$1: scala.reflect.ClassTag[A],↩
implicit evidence$2: reflect.runtime.universe.TypeTag[A])↩
(scala.reflect.ClassTag[A], reflect.runtime.universe.TypeTag[A])
scala> m[List[Int]]
res36: (scala.reflect.ClassTag[List[Int]],↩
reflect.runtime.universe.TypeTag[List[Int]]) =↩
(scala.collection.immutable.List,TypeTag[scala.List[Int]])
Pertanyaan yang tersisa sekarang adalah apa artinya WeakTypeTag
? Singkatnya, TypeTag
merupakan tipe beton (ini berarti hanya memungkinkan tipe instantiated sepenuhnya) sedangkan WeakTypeTag
hanya memungkinkan jenis apa pun. Sebagian besar waktu seseorang tidak peduli yang mana (yang berarti TypeTag
harus digunakan), tetapi misalnya, ketika makro digunakan yang harus bekerja dengan jenis generik mereka diperlukan:
object Macro {
import language.experimental.macros
import scala.reflect.macros.Context
def anymacro[A](expr: A): String = macro __anymacro[A]
def __anymacro[A : c.WeakTypeTag](c: Context)(expr: c.Expr[A]): c.Expr[A] = {
// to get a Type for A the c.WeakTypeTag context bound must be added
val aType = implicitly[c.WeakTypeTag[A]].tpe
???
}
}
Jika seseorang mengganti WeakTypeTag
dengan TypeTag
kesalahan dilemparkan:
<console>:17: error: macro implementation has wrong shape:
required: (c: scala.reflect.macros.Context)(expr: c.Expr[A]): c.Expr[String]
found : (c: scala.reflect.macros.Context)(expr: c.Expr[A])(implicit evidence$1: c.TypeTag[A]): c.Expr[A]
macro implementations cannot have implicit parameters other than WeakTypeTag evidences
def anymacro[A](expr: A): String = macro __anymacro[A]
^
Untuk penjelasan yang lebih terperinci tentang perbedaan antara TypeTag
dan WeakTypeTag
melihat pertanyaan ini: Scala Macros: "tidak dapat membuat TypeTag dari tipe T yang memiliki parameter tipe yang belum terselesaikan"
Situs dokumentasi resmi Scala juga berisi panduan untuk Refleksi .