Написание общей функции трансляции Scala
Я пытаюсь написать метод, который выдает значение Any для определенного типа и возвращает параметр вместо того, чтобы бросать исключение, например instanceOf. 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
У кого-то есть идея, почему?
Ответы
Ответ 1
На вашем параде стираются дожди. Поэтому во время выполнения тип A больше не известен, а asInstanceOf[A]
скомпилирован в no-op. Это просто заставляет компилятор полагать, что результирующее значение имеет тип A, но на самом деле это не обеспечивается во время выполнения.
Вы можете использовать манифесты Scala, чтобы обойти его. К сожалению, обработка 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
}
Ответ 2
Это из-за стирания типа. Во время выполнения 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>)
// ...
Ответ 3
Я сделал почти то же самое сейчас, с 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