Написание обобщенной функции приведения Scala - PullRequest
17 голосов
/ 15 августа 2011

Я пытаюсь добиться написания метода, который преобразует значение Any в определенный тип и возвращает опцию вместо того, чтобы выдавать исключение, например instanceOf Скала ведет себя не так, как я ожидал:

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

У кого-нибудь есть идея, почему?

Ответы [ 3 ]

21 голосов
/ 15 августа 2011

Erasure идет дождь на вашем параде здесь. Поэтому во время выполнения тип A больше не известен, и asInstanceOf[A] компилируется в no-op. Это просто заставляет компилятор поверить, что результирующее значение имеет тип A, но на самом деле это не гарантируется во время выполнения.

Вы можете использовать манифесты Скалы, чтобы обойти это, однако. К сожалению, обработка JVM примитивных типов / бокса вынуждает нас сделать некоторую дополнительную работу.

Следующее работает, хотя оно не обрабатывает "слабое соответствие" типов, то есть, например, Int не считается длинным, поэтому cast[Long](42) возвращает None.

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
}
5 голосов
/ 15 августа 2011

Это из-за стирания типа.Во время выполнения A в Option[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>)
        // ...
2 голосов
/ 24 апреля 2013

Я сделал почти то же самое только сейчас, с Scala 2.10, TypeTags (из-за стирания типа) и ValidationNEL от scalaz:

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 as[T: TypeTag](term: Any): Option[T] =
  if (reflect.runtime.currentMirror.reflect(term).symbol.toType <:< typeOf[T])
    Some(term.asInstanceOf[T])
  else
    None

Я получил информацию здесь: Как узнать, является ли объект экземпляром типа TypeTag? , Разрешение во время выполнения аргументов типа с использованием отражения scala 2.10

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...