为什么需要@specialized
@specialized
是为了解决由于泛型擦除而需要对基础数据类型进行装箱/拆箱操作所带来的性能问题
@specialized
从 scala2.8
开始加入的,为什么要增加呢?
Java/Jvm的泛型是类型擦除的,也就是说泛型编译时会被丢弃,全部向上转型为 Object
, 因此对基础数据类型(primitive
) 来说, 无法直接使用基础数据类型
作为泛型参数,如 List<int>
是不可以的,所以就有了 Box
和 Unbox
, 将 int
装箱为 Integer
, double
装箱为 Double
, 等等。
带来的问题是内存的增加, double
本身只需要 8 个字节,装箱为 Double
后,将需要 24 个字节,对于一些有庞大集合的应用来说,很可能会带来内存的
性能瓶颈。
Scala由于需要编译为Java字节码,因此也就继承了Java/JVM这个缺陷,为了改进这个缺陷,Scala增加了 @specialized
注解,来指示Scala编译器来规避这个问题
@specialized 是如何解决的
首先,Scala语言标准库中定义了 specialized
来告知编译器针对泛型进行优化:
class specialized(group: SpecializedGroup) extends scala.annotation.StaticAnnotation {
def this(types: Specializable*) = this(new Group(types.toList))
def this() = this(Primitives)
}
其次,针对设置了 @specialized
的泛型类型,Scala编译器会针对特定的基础数据类型生成特定版本的代码,例如:
package eq
import scala.{specialized => sp}
trait Eq[@sp(Int) T] extends Any with Serializable {self =>
def eqv(x: T, y: T): Boolean
}
用 scalac -print
进行编译便可看见结果,如果 @sp
不指定类型,则会生成所有基础数据类型的特定版本。
需要注意的问题
固然 @specialized
通过特化版本的方式能够提高性能,但也是有缺点的:
- 会产生大量的特化版本的类,导致运行时需要加载更多的类型
- 如果有泛型依赖,被依赖中的泛型没有设置
specialized
就算设置了,也不会被特化,因此有一定的局限性
网友评论