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))
def?list(model:?Model)?=?{
//?get?current?user
val?userDetails?=?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代碼?
object?PredefineTest{
def?main(args:?Array[String]):Unit?=?{
val?c?:?Char?=?97.asInstanceOf[Char]
"hello".asInstanceOf[String]
1.asInstanceOf[Long]
val?it:?Seq[String]?=?List("a",?"b")
it.asInstanceOf[List[String]]
"hello".isInstanceOf[String]
classOf[String]
}
}
使用scalac?-Xprint:cleanup?PredefineTest.scala,Scala編譯器輸出的main方法體內代碼的抽象語法樹(AST)信息如下:
Scala代碼?
val?c:?Char?=?97.toChar();
("hello":?java.lang.String);
1.toLong();
val?it:?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代碼?
char?c?=?(char)97;
"hello";
1;
Seq?it?=?List..MODULE$.apply(Predef..MODULE$.wrapRefArray((Object[])new?String[]?{?"a",?"b"?}));
((List)it);
("hello"?instanceof?String);
String.class;
結合上面源碼來進行分析
classOf[T]
獲取類型T的Class對象
classOf方法定義在scala.Predef object:
Scala代碼?
object?Predef?extends?LowPriorityImplicits?{
def?classOf[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代碼?
final?class?Int?extends?AnyVal?{
...
def?toChar:?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目前的關注點不在此?