A TypeTag
, Scala tiplerinin çalışma zamanında silinmesi sorununu çözer (tip silme). Eğer yapmak istiyorsak
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"
}
uyarılar alacağız:
<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"
^
Bu sorunu çözmek için Manifestler Scala'ya tanıtıldı. Ancak, yola bağlı türler gibi birçok yararlı türü temsil edememe sorunu var:
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
Böylece, bunların her ikisi de kullanımı çok daha basit olan ve yeni Reflection API'sine iyi entegre olan TypeTags ile değiştirilir . Onlarla birlikte yola bağımlı türler hakkındaki sorunu zarif bir şekilde çözebiliriz:
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
Ayrıca tip parametrelerini kontrol etmek için kullanımı kolaydır:
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
Bu noktada, eşitlik kontrolleri için =:=
(tip eşitliği) ve <:<
(alt tip ilişkisi) kullanımını anlamak son derece önemlidir . İle asla temizlemeyin ==
veya !=
kesinlikle ne yaptığını bilmedikçe,:
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
İkincisi yapısal eşitliği kontrol eder, bu genellikle yapılması gereken şey değildir, çünkü önekler gibi şeyleri umursamaz (örnekteki gibi).
A TypeTag
tamamen derleyici tarafından üretilir, yani derleyici TypeTag
böyle bir a'yı bekleyen bir yöntemi çağırdığında bir derleyici oluşturur ve doldurur TypeTag
. Üç farklı etiket biçimi vardır:
ClassTag
yerine ClassManifest
ise TypeTag
daha fazla ya da daha az bir yedek Manifest
.
Birincisi genel dizilerle tam olarak çalışmayı sağlar:
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
yalnızca çalışma zamanında türler oluşturmak için gereken bilgileri sağlar (bunlar tür silinir):
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]
Yukarıda görüldüğü gibi, tip silme umurunda değil, bu yüzden biri "tam" türleri TypeTag
kullanılmalıdır:
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
Gördüğünüz gibi, tam tpe
olarak TypeTag
sonuç yöntemi denir Type
, bu da ne zaman elde typeOf
edilir. Tabii ki, her ikisini de kullanmak mümkündür, ClassTag
ve 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]])
Geriye kalan soru şimdi ne anlama geliyor WeakTypeTag
? Kısacası, TypeTag
somut bir türü temsil eder (bu sadece tamamen somutlaştırılmış tiplere izin verdiği anlamına gelir), WeakTypeTag
sadece herhangi bir türe izin verir. Çoğu zaman, hangisinin (hangi araçların TypeTag
kullanılması gerektiğini) önemsemez , ancak örneğin, genel türlerle çalışması gereken makrolar kullanıldığında ihtiyaç duyulur:
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
???
}
}
Biri hata WeakTypeTag
ile değiştirilirse TypeTag
:
<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]
^
Arasındaki farklar hakkında daha ayrıntılı bir açıklama için TypeTag
ve WeakTypeTag
: Bu soruya bakın “çözülmemiş tür parametreleri olan bir tip T den TypeTag oluşturamazsınız”: Scala Makrolar
Scala'nın resmi dokümantasyon sitesi aynı zamanda Yansıma için bir rehber içerir .