asInstanceOf[T]
将对象类型强制转换为T类型。
还是由于泛型存在类型擦除的原因,1.asInstanceOf[String]在运行时会抛出ClassCastException异常,而List(1).asInstanceOf[List[String]]将不会。
packageresti.web
importorg.springframework.beans.factory.annotation.Autowired
importorg.springframework.security.core.context.SecurityContextHolder
importorg.springframework.stereotype.Controller
importorg.springframework.ui.Model
importorg.springframework.web.bind.annotation.RequestMapping
importresti.domain.HttpApi
importresti.service.HttpApiRepository
importorg.springframework.security.core.userdetails.UserDetails
importorg.springframework.web.bind.annotation.RequestMethod
@Controller
@RequestMapping(Array("/product"))
classProductController@Autowired()(privatevalhttpApiRepository:HttpApiRepository){
@RequestMapping(method=Array(RequestMethod.GET))
deflist(model:Model)={
//getcurrentuser
valuserDetails=SecurityContextHolder.getContext().getAuthentication().getPrincipal().asInstanceOf[UserDetails]
model.addAttribute("currentUser",userDetails.getUsername)
model.addAttribute("products",httpApiRepository.findProducts())
"product/list"
}
}
classOf、isInstanceOf、asInstanceOf三个预定义方法分析
Scala的三个预定义(predefined)方法,我们经常用到;它们用来感觉很简单,
但是里面还是隐藏了一些细节东西,不妨花点时间来分析分析。
先上代码
PredefineTest.scala
Scala代码
objectPredefineTest{
defmain(args:Array[String]):Unit={
valc:Char=97.asInstanceOf[Char]
"hello".asInstanceOf[String]
1.asInstanceOf[Long]
valit:Seq[String]=List("a","b")
it.asInstanceOf[List[String]]
"hello".isInstanceOf[String]
classOf[String]
}
}
使用scalac-Xprint:cleanupPredefineTest.scala,Scala编译器输出的main方法体内代码的抽象语法树(AST)信息如下:
Scala代码
valc:Char=97.toChar();
("hello":java.lang.String);
1.toLong();
valit:Seq=immutable.this.List.apply(scala.this.Predef.wrapRefArray(Array[java.lang.String]{"a","b"}.$asInstanceOf[Array[java.lang.Object]]()));
it.$asInstanceOf[List]();
"hello".$isInstanceOf[java.lang.String]();
{
classOf[java.lang.String];
()
}
使用jd反编译工具查看对应代码如下:
Java代码
charc=(char)97;
"hello";
1;
Seqit=List..MODULE$.apply(Predef..MODULE$.wrapRefArray((Object[])newString[]{"a","b"}));
((List)it);
("hello"instanceofString);
String.class;
结合上面源码来进行分析
classOf[T]
获取类型T的Class对象
classOf方法定义在scala.Predef object:
Scala代码
objectPredefextendsLowPriorityImplicits{
defclassOf[T]:Class[T]=null
...
classOf的注释翻译过来的意思是:返回类型的运行时呈现状态。这是一个存根方法。实际的实现是由编译器填补(自动生成)。
Predef
object是默认导入的,所以classOf方法相当于一个全局方法。
isInstanceOf[T]
判断对象是否为T类型的实例。
isInstanceOf和asInstanceOf
由scala.Any类定义,Scala类层级的根类;其中class scala.AnyRef
继承自Any,是所有应引用类型的基类;trait scala.AnyVal
也继承自Any,是所有基本类型的实现的trait。所以所有对象都自动拥有isInstanceOf和asInstanceOf这两个方法。
特别注意的是Any 和AnyRef
这两个类属于“编译时类型”(虚拟类型?),不存在于运行时。所以这两者在Scala中都未提供源码,其语义由编译器在编译时构建。
再看一下例子:
Scala代码
scala>1.isInstanceOf[String]
res0:false
scala>List(1).isInstanceOf[List[String]]
res0:true
由于Scala像Java一样泛型存在类型擦除的原因,List(1).isInstanceOf[List[String]]及相当于List(1).isInstanceOf[List[_]],
List(1) 是List的实例.
asInstanceOf[T]
将对象类型强制转换为T类型。
还是由于泛型存在类型擦除的原因,1.asInstanceOf[String]在运行时会抛出ClassCastException异常,而List(1).asInstanceOf[List[String]]将不会。
在scala 讨论组里有人问道这样一个问题:
”I expect "new AnyRef().isInstanceOf[AnyVal]" to be false, but I
get true instead“
scala> new AnyRef().isInstanceOf[AnyVal]
res0: Boolean = true
大家有兴趣看以看看后面的解答,不过试了scala 2.9, 这种用法
已经被编译器禁止了:
scala> new AnyRef().isInstanceOf[AnyVal]
:8: error: type AnyVal
cannot be used in a type pattern or isInstanceOf test
new AnyRef().isInstanceOf[AnyVal]
还有,值得提一下的一个小细节就是,通过观察编译输出的AST,
知道对于在基本类型如Int等的对象上调用asInstanceOf[T],
Scala会将其转换为调用相应的toT方法, 如 1.asInstanceOf[Char], 就会转换为 97.toChar,
其中toChar 定义在 scala.Int:
Scala代码
finalclassIntextendsAnyVal{
...
deftoChar:Char=sys.error("stub")
...
}
而后, Scala编译器会进一步将其编译成与“(char)97”相同的字节码。
结论
总而言之,我们把classOf[T]看成Java里的T.class,
obj.isInstanceOf[T]看成 obj instanceof T,
obj.asInstanceOf[T]看成(T)obj就对了。scala为我们提供了语法糖,但也免不了类型擦除问题的影响。
值得探讨的地方
个人感觉,Scala对Java的类这一块没什么增强,
比如像Ruby一样类文字量也是对象(虽然理解起来有点绕,但是更能体现面向对象一致性),就不用classOf[T]这样添足的写法,而是:object.getClass
== String 。如此,是不是JVM的限制,还是Scala目前的关注点不在此?