Каковы различия между asInstanceOf [T] и (o: T) в Scala?

Я видел, что есть два метода для приведения объекта в Scala:

foo.asInstanceOf[Bar]
(foo: Bar)

Когда я попытался, я обнаружил, что asInstanceOf не использует неявное преобразование, в то время как другой делает.

Каковы различия в поведении между этими двумя методами? И где рекомендуется использовать один поверх другого?

47
задан aknuds1 24 February 2014 в 22:01
поделиться

3 ответа

  • foo.asInstanceOf [Bar] - это тип приведение , которое в первую очередь является операцией времени выполнения. В нем говорится, что компилятор следует заставить поверить, что foo является Bar . Это может привести к ошибке ( ClassCastException ), если и когда foo оценивается как нечто иное, чем Bar во время выполнения.

  • foo: Bar - это тип ascription , который полностью является операцией времени компиляции. Это помогает компилятору понять смысл вашего кода, не заставляя его верить чему-либо, что могло бы быть ложным; никакие сбои времени выполнения не могут возникнуть из-за использования описаний типов.

Описание типов также можно использовать для запуска неявных преобразований. Например, вы можете определить следующее неявное преобразование:

implicit def foo(s:String):Int = s.length

, а затем обеспечить его использование следующим образом:

scala> "hi":Int                                 
res29: Int = 2

Присвоение типа Int String обычно происходит во время компиляции введите ошибку, но прежде чем отказаться от нее, компилятор будет искать доступные неявные преобразования, чтобы устранить проблему.Конкретное неявное преобразование, которое будет использоваться в данном контексте, известно во время компиляции.

Излишне говорить, что ошибки во время выполнения нежелательны, поэтому чем больше вы можете указать параметры типобезопасным способом (без использования asInstanceof ), тем лучше! Если вы обнаружите, что используете asInstanceOf , вам, вероятно, следует использовать вместо него match .

84
ответ дан 26 November 2019 в 19:28
поделиться

Программирование на Scala рассказывает об этом немного подробнее в Главе 15 - Case Classes and Pattern Matching.

В основном, вторая форма может быть использована как "типизированный шаблон" в сопоставлении шаблонов, что дает функциональность isInstanceOf и asInstanceOf. Сравните

if (x.isInstanceOf[String]) {
  val s = x.asInstanceOf[String]
  s.length
} else ...

с

def checkFoo(x: Any) = x match {
  case s: String => s.length
  case m: Int => m
  case _ => 0
}

Авторы намекают, что многословность способа isInstance* намеренно подталкивает вас к стилю сопоставления с образцом.

Однако я не уверен, какой шаблон более эффективен для простого приведения типа без проверки.

8
ответ дан 26 November 2019 в 19:28
поделиться

Ответ Pelotom'а довольно хорошо охватывает теорию, вот несколько примеров для наглядности:

def foo(x: Any) {
  println("any")
}

def foo(x: String) {
  println("string")
}


def main(args: Array[String]) {
  val a: Any = new Object
  val s = "string"

  foo(a)                       // any
  foo(s)                       // string
  foo(s: Any)                  // any
  foo(a.asInstanceOf[String])  // compiles, but ClassCastException during runtime
  foo(a: String)               // does not compile, type mismatch
}

Как вы видите, приписывание типов может быть использовано для разрешения неоднозначностей. Иногда они могут быть неразрешимы компилятором (см. далее), который сообщит об ошибке, и вы должны ее разрешить. В других случаях (как в примере) просто используется "неправильный" метод, а не то, что вы хотите. foo(a: String) не компилируется, показывая, что приписывание типа не является приведением. Сравните это с предыдущей строкой, где компилятор доволен, но вы получаете исключение, так что ошибка обнаружена в приписывании типа.

Вы получите неразрешимую двусмысленность, если также добавите метод

def foo(xs: Any*) {
  println("vararg")
}

В этом случае первый и третий вызов foo не скомпилируются, так как компилятор не может решить, хотите ли вы вызвать foo с одним параметром Any или с varargs, так как оба варианта кажутся одинаково хорошими => вы должны использовать приписывание типа, чтобы помочь компилятору.

Редактировать см. также Какова цель приписывания типов в Scala?

16
ответ дан 26 November 2019 в 19:28
поделиться
Другие вопросы по тегам:

Похожие вопросы: