hangscer

关于scala中的Manifest部分使用说明

2018/09/22

Before

    scala中的隐式参数特性主要为了简化参数的传递,由runtime自动传递。比如:

1
2
3
4
5
implicit val theTragetSystem : ActorSystem = _ // init...
...
def doSthByAkka[T](value:T)(implicit system:ActorSystem)={
...
}

    以上代码中定义了doSthByAkka函数,该函数有两个参数,第二个参数可以由程序上下文搜寻ActorSystem的实例(隐式变量,theTragetSystem)自动传入,这也是最常规的做法。

Other

    Manifest机制是scala为了弥补jvm泛型参数擦除问题。对于jvm而言,classOf[List[String]]classOf[List[Int]]两者相等,即classOf[List[String]] == classOf[List[Int]]返回为true。对于Manifest而言,它并没有突破vm限制,jvm仍然把泛型擦除了,但是scala额外记录了泛型信息,比如manifest[List[String]] == manifest[List[Int]]返回为false,因为List[String]List[Int]本身就是两种类型(类与类型,不一定一致)。


    利用Manifest可以获取泛型参数的具体class,比如:

1
2
3
4
5
class MyTestClass[A] {
def getTypeParamClass() = classOf[A]
}
...
new MyTestClass[Int].getTypeParamClass() // use it. compile error!

    但是,以上的做法是不对的,因为泛型擦除了。于是需要利用Manifest即可,当我们需要查看泛型参数A的class时,我们需要在目标方法上(implicit man: Manifest[A])参数,该参数将由scala编译器自动传入:

1
2
3
4
5
6
7
8
class MyTestClass[A] {
def getTpeClass()(implicit man: Manifest[A]): Class[A] =
man
.runtimeClass
.asInstanceOf[Class[A]]
}
...
new MyTestClass[List[String]].getTpeClass() // class scala.collection.immutable.List


W

    但是,对于Manifest的使用,存在某些限制。比如对于某个类,该类有个泛型参数T,该类有两个方法A和B,B方法需要得到泛型T的class,但是A需要调用B,初步的实现如下:

1
2
3
4
5
6
7
8
9
10
class MyTestClass[A] {
def funB()(implicit manifest: Manifest[A]): String =
manifest.runtimeClass.getName
def funA() = {
println(funB())
}
}
... // compile error.
new MyTestClass[String].funA()

    以上代码编译错误的原因是A调用B,但是B需要Manifest[A]的隐式参数,而A却没有将其传递给B,那么流程就是编译器把隐式参数Manifest[A]传递给A,再由A传递给B。此时,我们可以说:Manifest[A]的作用域是存在函数中,而不是在该类中的任何地方都可以获取。所以以下可以给出两种解决方案,一种是处于调用链中的任何任一函数如果需要隐式的Manifest[A]参数,那么将该链上的所有函数的参数额外加上(implicit manifest: Manifest[A])即可;或者,直接将Manifest[A]声明在类的作用域上:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class MyTestClass[A] {
def funB()(implicit manifest: Manifest[A]): String =
manifest.runtimeClass.getName
def funA()(implicit manifest: Manifest[A]) = {
println(funB())
}
}
... // or
class MyTestClass[A:Manifest] {
def funB(): String =
manifest[A].runtimeClass.getName
def funA() = {
println(funB())
}
}