Jakie są różnice pomiędzy asInstanceOf[t] I (o: T) w Scali?

Widziałem, że istnieją dwie metody rzucania obiektu w Scali:

foo.asInstanceOf[Bar]
(foo: Bar)

Kiedy próbowałem, odkryłem, że asInstanceOf nie używa konwersji implicit, podczas gdy druga.

Jakie są różnice w zachowaniu pomiędzy tymi dwiema metodami? A gdzie jest zalecane stosowanie jednego nad drugim?

Author: aknuds1, 2010-08-05

4 answers

  • foo.asInstanceOf[Bar] jest typem cast , który jest przede wszystkim operacją runtime. Mówi, że kompilator powinien być zmuszony do przekonania, że foo jest Bar. Może to spowodować błąd (a ClassCastException), jeśli i kiedy foo jest oceniane jako coś innego niż Bar w czasie wykonywania.

  • foo:Bar jest typem ascription , który jest w całości operacją w czasie kompilacji. Daje to kompilatorowi pomoc w zrozumieniu znaczenia kodu, bez wymuszania wierzy we wszystko, co może być nieprawdziwe; żadne błędy runtime nie mogą wynikać z użycia przypisań typu.

Przypisania typów mogą być również użyte do wywołania konwersji niejawnych. Na przykład można zdefiniować następującą konwersję implicit:

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

A następnie zapewnić jego użycie w ten sposób:

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

Przypisanie typu Int do String normalnie byłoby błędem typu w czasie kompilacji, ale przed rezygnacją kompilator będzie szukał dostępnych domyślnych konwersje, aby problem zniknął. Konkretna ukryta konwersja, która będzie używana w danym kontekście, jest znana podczas kompilacji.

Nie trzeba dodawać, że błędy uruchomieniowe są niepożądane, więc zakres, w jakim można określić rzeczy w sposób type-safe (bez użycia asInstanceof), tym lepiej! Jeśli używasz asInstanceOf, prawdopodobnie powinieneś używać match.

 80
Author: Tom Crockett,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-14 02:47:46

Odpowiedź Pelotoma obejmuje teorię całkiem nieźle, oto kilka przykładów, aby było to jaśniejsze:

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
}

Jak widać, rodzaj recepty może być używany do rozwiązywania disambiguations. Czasami mogą być nierozwiązywalne przez kompilator (patrz później), który zgłosi błąd i musisz go rozwiązać. W innych przypadkach (jak w przykładzie) po prostu używa metody "wrong", a nie, że chcesz. foo(a: String) nie kompiluje, pokazując, że typ ascription nie jest obsadą. Porównaj go z poprzednim line, gdzie kompilator jest szczęśliwy, ale dostajesz wyjątek, więc błąd jest wykrywany wtedy z typem ascription.

Otrzymasz nierozwiązywalną wieloznaczność, jeśli dodasz również metodę

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

W tym przypadku pierwsze i trzecie wywołanie foo nie będzie kompilowane, ponieważ kompilator nie może zdecydować, czy chcesz wywołać foo z pojedynczym param, czy z varargami, ponieważ oba wydają się być równie dobre => musisz użyć przypisania typu, aby pomóc w wywołaniu foo. kompilator.

Edit Zobacz także Jaki jest cel zapisu typu w Scali?

 16
Author: Sandor Murakozi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:02:33

Programowanie w Scali opisuje to nieco szczegółowo w rozdziale 15 - klasy przypadków i dopasowywanie wzorców .

Zasadniczo druga forma może być używana jako "Typed Pattern" w dopasowaniu wzorca, dając funkcjonalność isInstanceOf i asInstanceOf. Porównaj

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

Vs.

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

Autorzy sugerują, że zwięzłość sposobu robienia rzeczy jest zamierzona, aby popchnąć cię do stylu dopasowania wzorców.

Nie jestem pewien, który wzór jest bardziej skuteczny dla prostego odlewu bez testu.

 8
Author: Jon McAuliffe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-19 08:51:49

Jest przykład różnicy:

  1. Typ cast (asInstanceOf) jest operacją run-time z potencjalnie wyjątkiem run-time.
  2. Ascription jest w zasadzie tylko up-cast wykonywany w czasie kompilacji.

Przykład:

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

Możemy również wywołać metodę używając multiple-dispatch:

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"

Możemy zdefiniować konwersję implicit:

// 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)
 2
Author: Lukáš Janeček,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-11-28 22:02:28