Каковы различия между asInstanceOf [T] и (o: T) в Scala? - PullRequest
47 голосов
/ 05 августа 2010

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

foo.asInstanceOf[Bar]
(foo: Bar)

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

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

Ответы [ 4 ]

81 голосов
/ 05 августа 2010
  • foo.asInstanceOf[Bar] - это тип cast , который в основном является операцией во время выполнения. Это говорит о том, что компилятор должен быть вынужден поверить, что 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.

16 голосов
/ 05 августа 2010

Ответ Пелотома довольно неплохо описывает теорию, вот несколько примеров, поясняющих ситуацию:

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) не компилируется, показывая, что тип ascription не приведен. Сравните это с предыдущей строкой, где компилятор доволен, но вы получите исключение, поэтому ошибка обнаруживается затем с типом ascription.

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

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

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

Редактировать см. Также Какова цель начертания шрифтов в Scala?

8 голосов
/ 05 августа 2010

Программирование в 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* способа ведения дел является намеренным подтолкнуть вас к стилю сопоставления с образцом.

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

2 голосов
/ 29 ноября 2016

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

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

Пример:

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)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...