2011-08-14 17 views
17

Any型の値を特定の型にキャストし、instanceOfのような例外をスローする代わりにオプションを返すメソッドを記述しようとしています。私はそれを期待しているようにScalaは動作しません:ジェネリックキャスト関数の記述Scala

def cast[A](value: Any): Option[A] = 
{ 
    try 
    { 
    Some(value.asInstanceOf[A]) 
    } catch 
    { 
    case e: Exception => None 
    } 
} 

テストを:

val stringOption: Option[String] = cast[String](2) 
stringOption must beNone 

がエラーで失敗し

java.lang.Exception: 'Some(2)' is not None 

誰かがアイデア理由がありますか?

+0

整数値を文字列にキャストすると例外が発生するため、このメソッドはNoneを返しますが、そうではありません。私はscala 2.9.0-1を使用します。 –

+0

はい、それはいくつか(2)を返しますが、そうではありません。値を 'get'しようとすると例外がスローされますが、' getOrElse'は大丈夫です。 –

+0

まあまあ、私は例外がキャストメソッドで発生することを期待していました。 –

答えて

21

消去の雨。したがって、実行時にタイプAはもう認識されず、asInstanceOf[A]はno-opにコンパイルされます。コンパイラは結果の値がタイプAであると信じさせますが、実際には実行時には保証されません。

しかし、Scalaのマニフェストを使って回避することができます。残念なことに、JVMのプリミティブ型/ボクシングの処理では、余分な作業が必要になります。

タイプの「弱い順応」は扱えませんが、次のようなことがあります。 IntはLongと見なされないため、cast[Long](42)Noneを返します。それは次のようになり代わりに検証するオプションを

import scala.reflect.runtime.universe._ 

def as[T: TypeTag](term: Any): ValidationNEL[String, T] = 
    if (reflect.runtime.currentMirror.reflect(term).symbol.toType <:< typeOf[T]) 
    term.asInstanceOf[T].successNel[String] 
    else 
    ("Cast error: " + term + " to " + typeOf[T]).failNel[T] 

def cast[A : Manifest](value: Any): Option[A] = { 
    val erasure = manifest[A] match { 
    case Manifest.Byte => classOf[java.lang.Byte] 
    case Manifest.Short => classOf[java.lang.Short] 
    case Manifest.Char => classOf[java.lang.Character] 
    case Manifest.Long => classOf[java.lang.Long] 
    case Manifest.Float => classOf[java.lang.Float] 
    case Manifest.Double => classOf[java.lang.Double] 
    case Manifest.Boolean => classOf[java.lang.Boolean] 
    case Manifest.Int => classOf[java.lang.Integer] 
    case m => m.erasure 
    } 
    if(erasure.isInstance(value)) Some(value.asInstanceOf[A]) else None 
} 
+0

それは真実ではありません - asInstanceOfはプリミティブでないタイプとジェネリック – dk14

5

これは、タイプ消去のためです。実行時にAOption[A]であるため、Some(3)を変数Option[String]に格納することは許可されています。

例外は、オプション内の値がアクセスしたときに発生します。ここにあなたのオンパレード

scala> val result = cast[String](2) 
result: Option[String] = Some(2) 

scala> result.get 
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String 
     at .<init>(<console>:10) 
     at .<clinit>(<console>) 
     // ... 
+0

なぜ 'getOrElse(42)'はこの例外を生成しませんが、2に評価しますか? –

+2

'Option [A] .getOrElse [B]'の戻り値の型は、 'A'と' B'の両方のスーパータイプでなければなりません。 'String'と' Int'の場合、戻り値の型は 'Any'です。もちろん' 2'を 'Any'にキャストすることもできます。 'getOrElse(" 42 ")'を試した場合、戻り値の型は 'String'であるため、' ClassCastException'が返されます。 –

+0

意味があるので、一般的な方法でそのようなメソッドを書く簡単な方法はありませんか? –

2

私はscalazからScalaの2.10、(原因型消去に)TypeTagsとValidationNELで、今ほとんど同じことをしました:

私はここに私に関する情報を得た
def as[T: TypeTag](term: Any): Option[T] = 
    if (reflect.runtime.currentMirror.reflect(term).symbol.toType <:< typeOf[T]) 
    Some(term.asInstanceOf[T]) 
    else 
    None 

How to know if an object is an instance of a TypeTag's type?Runtime resolution of type arguments using scala 2.10 reflection

関連する問題