2016-09-19 72 views
3

我想建立一個isInstanceOf[T]asInstanceOf[T]對的包裝,將輸出Option[T]方便mapgetOrElse方法。什麼是包裝isInstanceOf []調用的正確方法?

所以我試一試,但結果令我失望。

import scala.reflect.runtime.universe.{TypeTag, typeOf} 

class Base() 
class Deep() extends Base 
class Deeper() extends Deep() 

final case class WrapSimple[T](source : T) { 
    def cast[U] : Option[U] = 
    if (source.isInstanceOf[U]) Some(source.asInstanceOf[U]) else None 
} 

final case class WrapFullTagged[T: TypeTag](source : T) { 
    def cast[U : TypeTag] : Option[U] = 
    if (typeOf[T] <:< typeOf[U]) Some(source.asInstanceOf[U]) else None 
} 

final case class WrapHalfTagged[T](source : T) { 
    val stpe = { 
    val clazz = source.getClass 
    val mirror = scala.reflect.runtime.universe.runtimeMirror(clazz.getClassLoader) 
    mirror.classSymbol(clazz).toType 
    } 
    def cast[U : TypeTag] : Option[U] = 
    if (stpe <:< typeOf[U]) Some(source.asInstanceOf[U]) else None 
} 

object Test { 
    val base = new Base 
    val deep = new Deep 
    val deeper = new Deeper 
    val wile : Deep = new Deeper 

    def testSimple() : Unit = { 
    println(WrapSimple(deep).cast[Base].isDefined) // should be true 
    println(WrapSimple(deep).cast[Deeper].isDefined) // should be false 
    println(WrapSimple(wile).cast[Deeper].isDefined) // should be true 
    } 

    def testFullTagged() : Unit = { 
    println(WrapFullTagged(deep).cast[Base].isDefined) // should be true 
    println(WrapFullTagged(deep).cast[Deeper].isDefined) // should be false 
    println(WrapFullTagged(wile).cast[Deeper].isDefined) // should be true 
    } 

    def testHalfTagged() : Unit = { 
    println(WrapHalfTagged(deep).cast[Base].isDefined) // should be true 
    println(WrapHalfTagged(deep).cast[Deeper].isDefined) // should be false 
    println(WrapHalfTagged(wile).cast[Deeper].isDefined) // should be true 
    } 

    def testAll() : Unit = { 
    testSimple() 
    testFullTagged() 
    testHalfTagged() 
    } 
} 

WrapSimple看起來不錯,但只是不工作,它會清除在isInstanceOf[U]方法應用U類型,所以它總是與true響應。有趣的是,asInstanceOf[U]保持U類型正常,所以它只是產生運行時異常。

我試過的第二種方法是使用類型標籤的WrapFullTagged。這似乎很清楚,但再次明顯地打破了合同。它只能在編譯時檢查靜態類型,並且對運行時的實際類型沒有深入瞭解。

因此,我培育了兩種方法並催生了第三種方法,至少能夠產生正確的輸出。但它看起來很糟糕,並且引起反思的力量,而這種反射的成本很高。

是否有可能通過更高雅的方式解決問題?

+1

在運行時你永遠不會對類型有更多的瞭解,因爲它們被擦除。在運行時你可以擁有類型信息的唯一方法是如果你在編譯時通過TypeTag來保存它。在第一個例子中,'T'和'U'都被擦除。你能說出一個用例,其中'WrapFullTagged'沒有提供你想要的結果嗎? –

+0

每次你想使用'isInstanceOf'。當您不知道運行時變量後面的實際類型時會發生這種情況。它經常發生。您可以嘗試記住您上次編寫項目時沒有任何匹配的模式。 – ayvango

+1

shapeless typeable https://github.com/milessabin/shapeless/blob/master/core/src/main/scala/shapeless/typeable.scala –

回答

2

結賬scala.reflect.ClassTag。它提供了式訪問擦除類類型,和according to the api docs用於功能

def unapply(x: Any): Option[T] 

甲ClassTag [T]可以作爲僅類型T的對象相匹配的提取

的例如,該預期輸出相匹配的問題,並出現合理的優雅:

class Base() 
class Deep() extends Base 
class Deeper() extends Deep() 

case object SimpleCaster { 
    def cast[A](t: Any)(implicit classTag: scala.reflect.ClassTag[A]): Option[A] = classTag.unapply(t) 
} 

object Test { 
    val base = new Base 
    val deep = new Deep 
    val deeper = new Deeper 
    val wile: Deep = new Deeper 

    def testSimple(): Unit = { 
    val a = SimpleCaster.cast[Base](deep) 
    val b = SimpleCaster.cast[Deeper](deep) 
    val c = SimpleCaster.cast[Deeper](wile) 
    println(s"${a.isDefined} - ${a.map(_.getClass)}") 
    println(s"${b.isDefined} - ${b.map(_.getClass)}") 
    println(s"${c.isDefined} - ${c.map(_.getClass)}") 
    } 
} 

導致控制檯輸出:

scala> Test.testSimple 
true - Some(class Deep) 
false - None 
true - Some(class Deeper) 

綜上所述;雖然這使用了反射apis,但它看起來不是太冗長的實用解決方案。

相關問題