Skip to content

Commit 2df8764

Browse files
[zh-cn]add reflect tags (#1783)
1 parent d3d1550 commit 2df8764

File tree

4 files changed

+181
-2
lines changed

4 files changed

+181
-2
lines changed

_overviews/reflection/thread-safety.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ overview-name: Reflection
66

77
num: 6
88

9-
languages: [ja]
9+
languages: [ja, zh-cn]
1010
permalink: /overviews/reflection/:title.html
1111
---
1212

_overviews/reflection/typetags-manifests.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ overview-name: Reflection
66

77
num: 5
88

9-
languages: [ja]
9+
languages: [ja, zh-cn]
1010
permalink: /overviews/reflection/:title.html
1111
---
1212

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
---
2+
layout: multipage-overview
3+
title: 线程安全
4+
partof: reflection
5+
overview-name: Reflection
6+
7+
num: 6
8+
language: zh-cn
9+
---
10+
11+
<span class="label important" style="float: right;">EXPERIMENTAL</span>
12+
13+
**Eugene Burmako**
14+
15+
遗憾的是,在scala2.10.0发布的现行状态下,反射不是线程安全的。
16+
这里有个JIRA问题 [SI-6240](https://issues.scala-lang.org/browse/SI-6240),它可以用来跟踪我们的进度和查找技术细节,下面是最新技术的简要总结。
17+
18+
<p><span class="label success">NEW</span>Thread safety issues have been fixed in Scala 2.11.0-RC1, but we are going to keep this document available for now, since the problem still remains in the Scala 2.10.x series, and we currently don't have concrete plans on when the fix is going to be backported.</p>
19+
20+
目前,在反射相关方面存在两种竞争状态。 第一是反射的初始化(当调用代码首次访问`scala.reflect.runtime.universe`时)无法从多个线程安全地调用。第二是符号的初始化(当调用代码第一次访问符号的标记或类型签名时)也不安全。
21+
这是一个典型的表现:
22+
23+
java.lang.NullPointerException:
24+
at s.r.i.Types$TypeRef.computeHashCode(Types.scala:2332)
25+
at s.r.i.Types$UniqueType.<init>(Types.scala:1274)
26+
at s.r.i.Types$TypeRef.<init>(Types.scala:2315)
27+
at s.r.i.Types$NoArgsTypeRef.<init>(Types.scala:2107)
28+
at s.r.i.Types$ModuleTypeRef.<init>(Types.scala:2078)
29+
at s.r.i.Types$PackageTypeRef.<init>(Types.scala:2095)
30+
at s.r.i.Types$TypeRef$.apply(Types.scala:2516)
31+
at s.r.i.Types$class.typeRef(Types.scala:3577)
32+
at s.r.i.SymbolTable.typeRef(SymbolTable.scala:13)
33+
at s.r.i.Symbols$TypeSymbol.newTypeRef(Symbols.scala:2754)
34+
35+
好消息是,编译时反射(通过`scala.reflect.macros.Context`暴露给宏的那一种)比运行时反射(通过`scala.reflect.runtime.universe`暴露出的那一种)更不容易受到线程问题的影响。
36+
第一个原因是,当宏有机会运行时,编译时反射`universe`已经初始化,这规避了我们的竞争条件1。第二个理由是至今为止没有编译程序本身就是线程安全,所以没有并行执行的工具。但是,如果您的宏产生了多个线程,则你仍应该小心。
37+
38+
39+
不过,对于运行时反射来说,情况要糟糕得多。首次初始化`scala.reflect.runtime.universe`时,称为反射初始化,而且这种初始化可以间接发生。
40+
此处最突出的示例是,调用带有`TypeTag`上下文边界的方法可能会带来问题,因为调用这种方法,Scala通常需要构造一个自动生成的类型标签,该标签需要创建一个类型,并需要初始化反射`universe`
41+
这个结果是,如果不采取特殊措施,就无法在测试中可靠地调用基于`TypeTag`的方法,这是因为sbt等很多工具并行执行测试。
42+
43+
汇总:
44+
* 如果您正在编写一个没有显式创建线程的宏那就没有问题。
45+
* 线程或参与者(actors)混在一起的运行时反射可能很危险。
46+
* 多个带有`TypeTag`上下文边界的线程调用方法可能导致不确定的结果。
47+
* 请查看 [SI-6240](https://issues.scala-lang.org/browse/SI-6240),以了解我们在此问题上的进展。
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
---
2+
layout: multipage-overview
3+
title: TypeTags 和 Manifests
4+
partof: reflection
5+
overview-name: Reflection
6+
7+
num: 5
8+
languages: zh-cn
9+
---
10+
11+
与其他JVM语言一样,Scala的类型在运行时被擦除。这意味着,如果要检查某个实例的运行时类型,则可能无法访问Scala编译器在编译时可用的所有类型信息。
12+
13+
`scala.reflect.Manifest``TypeTags`可以看作是将编译时可用的所有类型信息携带到运行时的对象。
14+
例如,`TypeTag[T]`封装了某个编译时类型`T`的运行时类型表示。但是请注意,`TypeTag`应该被认为是对2.10之前的`Manifest`概念的更丰富的替代品,后者还与Scala反射完全集成。
15+
16+
有三种不同类型的类型标记:
17+
18+
1. `scala.reflect.api.TypeTags#TypeTag`
19+
Scala类型的完整类型描述符。例如,`TypeTag[List[String]]`包含所有类型信息,在本例中是类型`scala.List[String]`
20+
21+
2. `scala.reflect.ClassTag`
22+
Scala类型的部分类型描述符。例如,`ClassTag[List[String]]`只包含已擦除、关于类的类型信息,在本例中为`scala.collection.immutable.List``ClassTag`只提供对类型的运行时类的访问。其类似于`scala.reflect.ClassManifest`
23+
24+
3. `scala.reflect.api.TypeTags#WeakTypeTag`
25+
抽象类型的类型描述符(参见下面相应的小节)。
26+
27+
## 获取`TypeTag`
28+
29+
`Manifest`类似,`TypeTag`总是由编译器生成,可以通过三种方式获得。
30+
31+
### 通过方法`typeTag``classTag``weakTypeTag`
32+
33+
通过使用通过`Universe`提供的方法`typeTag`,就可以直接获得特定类型的`TypeTag`
34+
35+
例如,要获取表示`Int``TypeTag`,我们可以执行以下操作:
36+
37+
import scala.reflect.runtime.universe._
38+
val tt = typeTag[Int]
39+
40+
或者类似地,要获得表示`String``ClassTag`,我们可以执行以下操作:
41+
42+
import scala.reflect._
43+
val ct = classTag[String]
44+
45+
这些方法中的每个方法都为给定的类型参数`T`构造一个`TypeTag[T]``ClassTag[T]`
46+
47+
### 使用类型为`TypeTag[T]``ClassTag[T]``WeakTypeTag[T]`的隐式参数
48+
49+
`Manifest`一样,实际上可以 _请求_ 编译器生成`TypeTag`。这只需指定一个类型为`TypeTag[T]`的隐式 _证据_ 参数即可完成。如果编译器在隐式搜索期间找不到匹配的隐式值,它将自动生成一个`TypeTag[T]`
50+
51+
_注意_:这通常是通过在方法上使用隐式参数来实现的,并且只能在类上。
52+
53+
例如,我们可以编写一个方法,它可以接受任意对象,并且使用`TypeTag`打印有关该对象的类型参数的信息:
54+
55+
import scala.reflect.runtime.universe._
56+
57+
def paramInfo[T](x: T)(implicit tag: TypeTag[T]): Unit = {
58+
val targs = tag.tpe match { case TypeRef(_, _, args) => args }
59+
println(s"type of $x has type arguments $targs")
60+
}
61+
62+
这里,我们在`T`上编写了一个参数化的泛型方法`paramInfo`,并提供了一个隐式参数`(implicit tag: TypeTag[T])`
63+
那我们就可以使用`TypeTag`的方法`tpe`直接访问`tag`表示的类型(`type`类型)。
64+
65+
然后,我们可以使用方法`paramInfo`,如下所示:
66+
67+
scala> paramInfo(42)
68+
type of 42 has type arguments List()
69+
70+
scala> paramInfo(List(1, 2))
71+
type of List(1, 2) has type arguments List(Int)
72+
73+
### 使用类型参数的上下文绑定
74+
75+
要实现与上述完全相同的效果,一种不太冗长的方法是在类型参数上使用上下文绑定。不需要提供单独的隐式参数,只需在类型参数列表中包含`TypeTag`,如下所示:
76+
77+
def myMethod[T: TypeTag] = ...
78+
79+
给定上下文绑定的`[T: TypeTag]`,编译器只需生成类型为`TypeTag[T]`的隐式参数,这将重写方法以进行查找,就像上一节中使用隐式参数的示例一样。
80+
81+
上面重写为使用上下文边界的示例如下:
82+
83+
84+
import scala.reflect.runtime.universe._
85+
86+
def paramInfo[T: TypeTag](x: T): Unit = {
87+
val targs = typeOf[T] match { case TypeRef(_, _, args) => args }
88+
println(s"type of $x has type arguments $targs")
89+
}
90+
91+
scala> paramInfo(42)
92+
type of 42 has type arguments List()
93+
94+
scala> paramInfo(List(1, 2))
95+
type of List(1, 2) has type arguments List(Int)
96+
97+
## WeakTypeTags
98+
99+
`WeakTypeTag[T]`泛化了`TypeTag`(意思是`TypeTag`是继承自`WeakTypeTag`的),`WeakTypeTag`与普通的`TypeTag`不同,
100+
101+
其类型表示的组件可以是对类型参数或抽象类型的引用。但是,`WeakTypeTag[T]`试图尽可能的具体(意思是如果都存在则优先更加具体的类型(参数)),也就是说,如果类型标记可用于被引用的类型参数或抽象类型,则它们用于将具体类型嵌入到`WeakTypeTag[T]`中。
102+
103+
继续上面的例子:
104+
105+
def weakParamInfo[T](x: T)(implicit tag: WeakTypeTag[T]): Unit = {
106+
val targs = tag.tpe match { case TypeRef(_, _, args) => args }
107+
println(s"type of $x has type arguments $targs")
108+
}
109+
110+
scala> def foo[T] = weakParamInfo(List[T]())
111+
foo: [T]=> Unit
112+
113+
scala> foo[Int]
114+
type of List() has type arguments List(T)
115+
116+
## TypeTags and Manifests
117+
118+
`TypeTag`可以大致对应2.10之前的`scala.reflect.Manifest`、 虽然`scala.reflect.ClassTag`对应于`scala.reflect.ClassManifest``scala.reflect.api.TypeTags#TypeTag`主要对应于`scala.reflect.Manifest`,但其他2.10版之前的`Manifest`类型与2.10版的`Tag`类型没有直接对应关系。
119+
120+
- **不支持scala.reflect.OptManifest。**
121+
这是因为`Tag`可以具体化任意类型,所以它们总是可用的。
122+
123+
- **没有对应的scala.reflect.AnyValManifest。**
124+
取而代之的是,可以将其`Tag`与基本`Tag`之一(在相应的伴随对象中定义)进行比较,以找出其是否代表原始值类。此外,可以简单地使用`<tag>.tpe.typeSymbol.isPrimitiveValueClass`
125+
126+
- **无法替换Manifest伴随对象中定义的工厂方法。**
127+
取而代之的是,可以使用Java(用于类)和Scala(用于类型)提供的反射API生成相应的类型。
128+
129+
- **不支持某些manifest操作(即`<:<`, `>:>``typeArguments`)。**
130+
取而代之的是,可以使用Java(用于类)和Scala(用于类型)提供的反射API。
131+
132+
在Scala 2.10中,不建议使用`scala.reflect.ClassManifest`,而推荐使用`TypeTag``ClassTag`,并且计划在即将发布的版本中弃用`scala.reflect.Manifest`。因此,建议迁移任何基于`Manifest`的API以使用`Tag`

0 commit comments

Comments
 (0)