[英]Using asInstanceOf on Generic Types in Scala
說,我有一個這樣的課:
class Funky[A, B](val foo: A, val bar: B) {
override def toString: String = s"Funky($foo, $bar)"
}
使用一些看起來像這樣的方法:
def cast(t: Any): Option[Funky[A, B]] = {
if (t == null) None
else if (t.isInstanceOf[Funky[_, _]]) {
val o = t.asInstanceOf[Funky[_, _]]
for {
_ <- typA.cast(o.foo)
_ <- typB.cast(o.bar)
} yield o.asInstanceOf[Funky[A, B]]
} else None
}
isInstanceOf和asInstanceOf如何工作? Runtime沒有關於Funky中包含的實際類型的信息。 那么這段代碼是如何工作的呢? 有線索嗎?
讓我們把它拆開。
假設您以某種方式獲取實例typA: Typable[A]
和typB: Typable[B]
,以便typA
有一個方法
def cast(a: Any): Option[A] = ...
同樣適用於typB
。 如果參數確實是A
,那么cast
方法應返回Some[A]
否則返回None
。 顯然可以為原始類型Int
和String
構造這些實例(它們已經由庫提供)。
現在,您要使用typA
和typB
實現cast
了Funky[A, B]
null
檢查應該是清楚的,你可以在任何事情上執行它。 但接下來是第一個isInstanceOf
:
else if (t.isInstanceOf[Funky[_, _]]) {
請注意, Funky
的類型參數已被下划線替換。 這是因為Funky
的通用參數被擦除,並且在運行時不可用。 但是,我們仍然可以區分Funky[_, _]
和例如Map[_, _]
,因為參數化類型本身被保留,即使參數被擦除。 此外, isInstanceOf
甚至可以區分TreeMap
和HashMap
,即使兩個實例都具有編譯時類型Map
:運行時類型可用,它只是被遺忘的泛型參數。
同樣,一旦你知道t
是Funky
類型,你可以把它投入Funky[_, _]
,
val o = t.asInstanceOf[Funky[_, _]]
用存在類型替換泛型參數。 這意味着:你只知道有一些類型X
和Y
,使得o
類型為Funky[X, Y]
,但你不知道那些X
和Y
是什么。 但是,現在你至少知道o
有方法foo
和bar
(即使你不知道它們的返回類型是什么)。
但是現在你可以把o.foo
和o.bar
帶到typA.cast
和typeB.cast
。 monadic綁定左側的下划線表示您丟棄A
和B
類型的實例,這些實例以Some
包裝的方式返回。 您只關心兩個強制轉換都不返回None
:
for {
_ <- typA.cast(o.foo)
_ <- typB.cast(o.bar)
} yield /* ... */
如果其中一個強制轉換失敗並返回None
,則整個monadic表達式將計算為None
,因此該方法將返回None
,表示整體轉換為Funky[A, B]
失敗。 如果兩個演員都成功,那么我們知道o
確實是Funky[A, B]
,所以我們可以將o
投入Funky[A, B]
:
o.asInstanceOf[Funky[A, B]]
您可能想知道“這怎么可能,我們在運行時對A
和B
一無所知!”,但這沒關系,因為asInstanceOf
只是為了滿足編譯器的類型檢查階段。 它在運行時無法執行任何操作 ,因為它只能檢查Funky
部分,而不能檢查已擦除的參數A
和B
以下是該現象的簡短說明:
val m: Map[Long, Double] = Map(2L -> 100d)
val what = m.asInstanceOf[Map[Int, String]]
println("It compiles, and the program does not throw any exceptions!")
只需將其保存為腳本並將其提供給scala
解釋器即可。 它會在沒有投訴的情況下編譯和運行,因為asInstanceOf
對Map
之外的任何東西都是盲目的。 因此,第二個asInstanceOf
只是為了說服類型檢查器返回的值確實是Funky[A, B]
,並且程序員有責任不做出任何荒謬的聲明。
總結一下: isInstanceOf
是在運行時執行的操作(它檢查實例是否符合某些具體類型,並返回runtime-values true
- false
)。 asInstanceOf
有兩個不同的功能。 第一個(轉換為具體類Funky[_, _]
)在運行時具有副作用(強制轉換可能會失敗並拋出異常)。 第二個( asInstanceOf[Funky[A, B]]
)只在編譯時滿足類型檢查階段。
希望這有點幫助。
您可以檢查元素是否為特定類型,例如:
Funky(1, 2).foo.isInstanceOf[String] // false
Funky(1, 2).foo.isInstanceOf[Int] // true
但是,如果您嘗試檢查泛型類型,它將無法正常工作。 例如:
def check[A](x: Any) = x.isInstanceOf[A]
check[String](1) // true
check[String](Funky(1, 2).foo) // true
編譯器會給出一條警告消息,說明錯誤:
抽象類型A是未選中的,因為它是通過擦除來消除的
但是,您展示的代碼似乎是通過其他方法解決此問題:
_ <- typA.cast(o.foo)
_ <- typB.cast(o.bar)
沒有看到這些對象的實現,我猜他們有某種TypeTag
或ClassTag
並使用它。 這幾乎總是推薦的解決方法。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.