Написание генераторной функции Scala

17

Я пытаюсь написать метод, который выдает значение 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

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

    
задан Bastian Echterhölter 14.08.2011 в 22:14
источник
  • Приведение целочисленного значения в строку должно привести к исключению, и метод должен вернуть None, но это не так. Я использую scala 2.9.0-1 –  Bastian Echterhölter 14.08.2011 в 22:28
  • Да, он возвращает некоторые (2), но ... нет. Попытка получить значение вызывает исключение, но getOrElse в порядке. –  14.08.2011 в 22:30
  • Да, точно, я ожидал, что исключение произойдет в методе литья. –  Bastian Echterhölter 14.08.2011 в 22:32
  • Может ли это иметь какое-либо отношение к стиранию стилей? –  14.08.2011 в 22:33

3 ответа

21

На вашем параде стираются дожди. Поэтому во время выполнения тип 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
}
    
ответ дан Ruediger Keller 14.08.2011 в 23:01
источник
  • это не так - asInstanceOf всегда компилируется в операцию checkcast для не-примитивных типов и дженериков –  dk14 01.12.2014 в 05:20
5

Это из-за стирания типа. Во время выполнения 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>)
        // ...
    
ответ дан Ben James 14.08.2011 в 22:34
источник
  • Почему getOrElse (42) не генерирует это исключение, но оценивает значение 2? –  14.08.2011 в 22:34
  • Тип возврата Option [A] .getOrElse [B] должен быть супертипом как A, так и B. В случае String и Int возвращаемый тип - Any, и вы можете, конечно, сбрасывать 2 в Any. Если вы попробовали getOrElse («42»), вы получили бы ClassCastException, так как возвращаемый тип был бы String. –  Ben James 14.08.2011 в 22:40
  • Имеет смысл, поэтому нет простого способа написать такой метод в общем виде? –  Bastian Echterhölter 14.08.2011 в 22:42
  • Я собирался ответить «вы могли бы сделать это с помощью Манифоров» - ну, см. ответ Ruediger :) –  Ben James 14.08.2011 в 23:38
2

Я сделал почти то же самое сейчас, с 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 reflection     

ответ дан schlicht 23.04.2013 в 23:33
источник