簡體   English   中英

在Scala中的通用類型上使用asInstanceOf

[英]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 顯然可以為原始類型IntString構造這些實例(它們已經由庫提供)。

現在,您要使用typAtypB實現castFunky[A, B]

null檢查應該是清楚的,你可以在任何事情上執行它。 但接下來是第一個isInstanceOf

  else if (t.isInstanceOf[Funky[_, _]]) {

請注意, Funky的類型參數已被下划線替換。 這是因為Funky 的通用參數被擦除,並且在運行時不可用。 但是,我們仍然可以區分Funky[_, _]和例如Map[_, _] ,因為參數化類型本身被保留,即使參數被擦除。 此外, isInstanceOf甚至可以區分TreeMapHashMap ,即使兩個實例都具有編譯時類型Map :運行時類型可用,它只是被遺忘的泛型參數。

同樣,一旦你知道tFunky類型,你可以把它投入Funky[_, _]

    val o = t.asInstanceOf[Funky[_, _]]

用存在類型替換泛型參數。 這意味着:你只知道有一些類型XY ,使得o類型為Funky[X, Y] ,但你不知道那些XY是什么。 但是,現在你至少知道o有方法foobar (即使你不知道它們的返回類型是什么)。

但是現在你可以把o.fooo.bar帶到typA.casttypeB.cast monadic綁定左側的下划線表示您丟棄AB類型的實例,這些實例以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]]

您可能想知道“這怎么可能,我們在運行時對AB一無所知!”,但這沒關系,因為asInstanceOf只是為了滿足編譯器的類型檢查階段。 在運行時無法執行任何操作 ,因為它只能檢查Funky部分,而不能檢查已擦除的參數AB

以下是該現象的簡短說明:

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解釋器即可。 它會在沒有投訴的情況下編譯和運行,因為asInstanceOfMap之外的任何東西都是盲目的。 因此,第二個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)

沒有看到這些對象的實現,我猜他們有某種TypeTagClassTag並使用它。 這幾乎總是推薦的解決方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM