2010-09-24 11 views
28

Mam następujący kod Scala.Scala pattern matching confusion with Option [Any]

import scala.actors.Actor 

object Alice extends Actor { 
    this.start 
    def act{ 
    loop{ 
     react { 
     case "Hello" => sender ! "Hi" 
     case i:Int => sender ! 0 
     } 
    } 
    } 
} 
object Test { 
    def test = { 
    (Alice !? (100, "Hello")) match { 
     case i:Some[Int] => println ("Int received "+i) 
     case s:Some[String] => println ("String received "+s) 
     case _ => 
    } 
    (Alice !? (100, 1)) match { 
     case i:Some[Int] => println ("Int received "+i) 
     case s:Some[String] => println ("String received "+s) 
     case _ => 
    } 
    } 
} 

Po wykonaniu Test.test, pojawia się komunikat:

scala> Test.test 
Int received Some(Hi) 
Int received Some(0) 

ja spodziewałem wyjście

String received Some(Hi) 
Int received Some(0) 

Jakie jest wytłumaczenie?

Jako drugie pytanie, mam unchecked ostrzeżenia z powyższym następująco:

C:\scalac -unchecked a.scala 
a.scala:17: warning: non variable type-argument Int in type pattern Some[Int] is unchecked since it is eliminated by erasure 
     case i:Some[Int] => println ("Int received "+i) 
      ^
a.scala:18: warning: non variable type-argument String in type pattern Some[String] is unchecked since it is eliminated by erasure 
     case s:Some[String] => println ("String received "+s) 
      ^
a.scala:22: warning: non variable type-argument Int in type pattern Some[Int] is unchecked since it is eliminated by erasure 
     case i:Some[Int] => println ("Int received "+i) 
      ^
a.scala:23: warning: non variable type-argument String in type pattern Some[String] is unchecked since it is eliminated by erasure 
     case s:Some[String] => println ("String received "+s) 
      ^
four warnings found 

Jak mogę uniknąć ostrzeżenia?

EDYCJA: Dziękujemy za sugestie. pomysł Daniela jest ładny, ale nie wydaje się, aby pracować z typów generycznych, jak w poniższym przykładzie

def test[T] = (Alice !? (100, "Hello")) match { 
    case Some(i: Int) => println ("Int received "+i) 
    case Some(t: T) => println ("T received ") 
    case _ => 
} 

Poniższy błędu ostrzegawczy napotkanego: warning: abstract type T in type pattern T is unchecked since it is eliminated by erasure

Odpowiedz

36

Wynika to typ-skasowaniem. JVM nie zna żadnego parametru typu, z wyjątkiem tablic. Z tego powodu kod Scala nie może sprawdzić, czy Option jest Option[Int] lub Option[String] - ta informacja została usunięta.

Można naprawić swój kod w ten sposób, iż:

object Test { 
    def test = { 
    (Alice !? (100, "Hello")) match { 
     case Some(i: Int) => println ("Int received "+i) 
     case Some(s: String) => println ("String received "+s) 
     case _ => 
    } 
    (Alice !? (100, 1)) match { 
     case Some(i: Int) => println ("Int received "+i) 
     case Some(s: String) => println ("String received "+s) 
     case _ => 
    } 
    } 
} 

ten sposób nie testują co typ Option jest, ale co rodzaj jego zawartość są - przy założeniu, że nie ma żadnej treści. None przejdzie do domyślnego przypadku.

+0

Dzięki! Inne odpowiedzi też są dobre, w tym obejście sugerowane przez Kevina. Ale wydaje się to najbardziej eleganckim sposobem na naprawienie mojego kodu bez konieczności wielokrotnego przepisywania. – Jus12

+0

Czy możesz zaproponować podobne rozwiązanie dla typów ogólnych? jak w: 'test def [T] = (Alicja!? (100," Hello ")) dopasuj {case Some (t: T) => println (" T received "); case _ => println ("coś innego otrzymanego")} ' – Jus12

+1

@ Jus12 W ten sposób nie będzie działać. Będziesz musiał uzyskać 'm: Manifest [T]', a następnie coś w stylu 'case Some (t: T) if m.erasure.isAssignableFrom (t.getClass()) =>'. –

8

Wszelkie informacje o parametrach typu jest dostępne tylko przy kompilacji -czas, nie w czasie wykonywania (jest to znane jako wymazywanie typu). Oznacza to, że w czasie wykonywania nie ma różnicy między Option[String] i Option[Int], więc każdy wzór pasujący do typu Option[String] będzie również pasował do Option[Int], ponieważ w czasie działania oba są po prostu Option.

Ponieważ to prawie zawsze nie jest to, co masz zamiar, otrzymasz ostrzeżenie. Jedynym sposobem uniknięcia ostrzeżenia nie jest sprawdzenie typowego typu czegoś w czasie wykonywania (co jest w porządku, ponieważ to nie działa tak, jak tego chcesz).

Nie ma sposobu, aby sprawdzić, czy Option jest Option[Int] lub Option[String] przy starcie (inne niż kontrolowanie zawartości, jeśli jest to Some).

2

Jak już wspomniano, walczysz z wymazaniem tutaj.

Dla rozwiązania ... To normalne ze Scala aktora do definiowania klas przypadków dla każdego typu wiadomości najprawdopodobniej wysłać:

case class MessageTypeA(s : String) 
case class MessageTypeB(i : Int) 

object Alice extends Actor { 
    this.start 
    def act{ 
    loop{ 
     react { 
     case "Hello" => sender ! MessageTypeA("Hi") 
     case i:Int => sender ! MessageTypeB(0) 
     } 
    } 
    } 
} 
object Test { 
    def test = { 
    (Alice !? (100, "Hello")) match { 
     case Some(MessageTypeB(i)) => println ("Int received "+i) 
     case Some(MessageTypeA(s)) => println ("String received "+s) 
     case _ => 
    } 
    (Alice !? (100, 1)) match { 
     case Some(MessageTypeB(i)) => println ("Int received " + i) 
     case Some(MessageTypeA(s)) => println ("String received " + s) 
     case _ => 
    } 
    } 
}