Một cách TypeTag
giải quyết vấn đề mà các kiểu của Scala bị xóa trong thời gian chạy (kiểu xóa). Nếu chúng ta muốn làm
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"
}
chúng tôi sẽ nhận được cảnh báo:
<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"
^
Để giải quyết vấn đề này Bản kê khai đã được giới thiệu cho Scala. Nhưng họ có vấn đề là không thể đại diện cho nhiều loại hữu ích, như loại phụ thuộc vào đường dẫn:
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
Do đó, chúng được thay thế bằng TypeTags , cả hai đều đơn giản hơn nhiều để sử dụng và được tích hợp tốt vào API Reflection mới. Với chúng, chúng ta có thể giải quyết vấn đề ở trên về các loại phụ thuộc đường dẫn một cách thanh lịch:
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
Chúng cũng dễ sử dụng để kiểm tra các tham số loại:
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
Tại thời điểm này, điều cực kỳ quan trọng là phải hiểu để sử dụng =:=
(loại bằng) và <:<
(quan hệ phụ) để kiểm tra đẳng thức. Không bao giờ sử dụng ==
hoặc !=
, trừ khi bạn hoàn toàn biết những gì bạn làm:
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
Cái sau kiểm tra sự bằng nhau về cấu trúc, thường không phải là điều nên làm vì nó không quan tâm đến những thứ như tiền tố (như trong ví dụ).
A TypeTag
hoàn toàn do trình biên dịch tạo, điều đó có nghĩa là trình biên dịch tạo và điền vào TypeTag
khi người ta gọi một phương thức mong đợi như vậy TypeTag
. Có ba dạng thẻ khác nhau:
ClassTag
thay thế ClassManifest
trong khi TypeTag
ít nhiều là sự thay thế cho Manifest
.
Cái trước cho phép làm việc hoàn toàn với các mảng chung:
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
chỉ cung cấp thông tin cần thiết để tạo các loại trong thời gian chạy (loại đã bị xóa):
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]
Như người ta có thể thấy ở trên, họ không quan tâm đến việc xóa loại, do đó, nếu muốn TypeTag
sử dụng loại "đầy đủ" :
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
Như người ta có thể thấy, phương pháp tpe
của TypeTag
kết quả trong một đầy đủ Type
, đó là cùng chúng tôi nhận được khi typeOf
được gọi. Tất nhiên, có thể sử dụng cả hai ClassTag
và 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]])
Câu hỏi còn lại bây giờ là ý nghĩa của WeakTypeTag
cái gì? Nói tóm lại, TypeTag
đại diện cho một loại cụ thể (điều này có nghĩa là nó chỉ cho phép các loại được khởi tạo hoàn toàn) trong khi WeakTypeTag
chỉ cho phép bất kỳ loại nào. Hầu hết thời gian người ta không quan tâm đó là cái gì (có nghĩa là TypeTag
nên được sử dụng), nhưng ví dụ, khi macro được sử dụng sẽ hoạt động với các loại chung mà chúng cần:
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
???
}
}
Nếu thay máy WeakTypeTag
với TypeTag
một lỗi được ném:
<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]
^
Để được giải thích chi tiết hơn về sự khác biệt giữa TypeTag
và WeakTypeTag
xem câu hỏi này: Scala Macros: Có thể tạo TypeTag từ loại T có các tham số loại chưa được giải quyết.
Trang web tài liệu chính thức của Scala cũng chứa một hướng dẫn về Reflection .