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

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

foo.asInstanceOf[Bar]
(foo: Bar)

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

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

4 ответа

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

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

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

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

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

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

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

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

Ответ Пелотома охватывает теорию довольно хорошо, вот несколько примеров, чтобы прояснить ее:

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
}

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

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

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

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

Изменить см. Также Какова цель ввода типа в Scala?

Программирование в Scala описывает это немного подробнее в главе 15 - Классы дел и сопоставление с образцом.

По сути, вторая форма может использоваться как "Типизированный шаблон" в сопоставлении с шаблоном, давая 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* способ делать вещи намеренно подталкивает вас к стилю сопоставления с образцом.

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

Есть пример различия:

  1. Тип cast (asInstanceOf) - это операция во время выполнения с возможным исключением во время выполнения.
  2. По сути, Ascription - это просто приведение, выполненное во время компиляции.

Пример:

class Parent() { def method() {} }
class Child1 extends Parent() { def method1() {} }
class Child2 extends Parent() { def method2() {} }

// we return Parent type
def getChild1() : Parent = new Child1()
def getChild2() : Parent = new Child2()
def getChild()  : Child1 = new Child1()

(getChild1().asInstanceOf[Child1]).method1() // OK
(getChild1().asInstanceOf[Child2]).method2() // runtime ClassCastException

(getChild1() : Child2).method2() // compile-time error
(getChild2() : Child2).method2() // compile-time error
(getChild() : Parent).method1() // compile-time error
(getChild()).method()  // OK

// with asInstanceOf, we can cast to anything without compile-time error
getChild1().asInstanceOf[String] // runtime ClassCastException
getChild1().asInstanceOf[Int] // runtime ClassCastException

Мы также можем вызвать метод, используя несколько отправлений:

def prt(p: Parent) = println("parent")
def prt(ch: Child1) = println("child")

prt(new Parent()) // prints "parent"
prt((new Child1()) : Parent) // prints "parent"
prt(new Child1()) // prints "child"

prt(new Parent().asInstanceOf[Child1]) // runtime ClassCastException
prt(new Child1().asInstanceOf[Parent]) // prints "parent"

Мы можем определить неявное преобразование:

// after definition of implicit conversions
implicit def toChild1(p: Parent) : Child1 = new Child1()
implicit def toChild2(p: Parent) : Child2 = new Child2()

(getChild1() : Child2).method2() // OK - implicit conversion to Child2 in ascription
(getChild2() : Child2).method2() // OK - implicit conversion to Child2 in ascription
(getChild2()).method1() // OK - implicit conversion to Child1 when calling method1()
(getChild2()).method2() // OK - implicit conversion to Child2 when calling method2()
(getChild2() : Parent).method() // OK - no implicit conversion
(getChild() : Parent).method1() // OK - implicit conversion to Child1 when calling method()

getChild1().asInstanceOf[Int] // still runtime ClassCastException (no implicit conversion)
Другие вопросы по тегам