2011-08-14 5 views
17

Any 값을 특정 형식으로 변환하고 instanceOf와 같은 예외를 throw하는 대신 옵션을 반환하는 메서드를 작성하려고합니다. 일반 캐스트 함수 작성 스칼라

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

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

누군가가 아이디어 왜이 오류와 함께 실패

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

테스트 : 나는 그것을 기대 한 것처럼 스칼라가 작동하지 않는 이유는 무엇입니까?

+0

문자열에 정수 값을 캐스팅하면 예외가 발생하고 메서드는 없음을 반환해야하지만 그렇지 않은 경우가 있습니다. 스칼라 2.9.0-1을 사용합니다. –

+0

예, 일부 (2)를 반환하지만 ...하지 않습니다. 값을'get '하려고하면 예외가 발생하지만'getOrElse'는 괜찮습니다. –

+0

예, 캐스트 메서드에서 예외가 발생할 것으로 예상했습니다. –

답변

21

삭제 비가. 따라서 런타임에 유형 A는 더 이상 알 수 없으며 asInstanceOf[A]은 no-op로 컴파일됩니다. 컴파일러는 결과 값이 A 유형이지만 실제로 런타임에서는 보장되지 않는다고 생각합니다.

스칼라의 매니 페스트를 사용하여 문제를 해결할 수 있습니다. 불행하게도 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에서 스칼라 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