2010-09-24 26 views
28

J'ai le code Scala suivant.Scala motif confusion correspondant à l'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 _ => 
    } 
    } 
} 

Après avoir fait Test.test, je reçois la sortie:

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

j'attendais la sortie

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

Quelle est l'explication?

En deuxième question, je reçois des avertissements unchecked avec ce qui précède comme suit:

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 

Comment puis-je éviter les mises en garde?

EDIT: Merci pour les suggestions. L'idée de Daniel est agréable mais ne semble pas fonctionner avec des types génériques, comme dans l'exemple ci-dessous

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

Ce qui suit erreur avertissement est rencontré: warning: abstract type T in type pattern T is unchecked since it is eliminated by erasure

Répondre

36

Ceci est dû au type d'effacement. La machine virtuelle Java ne connaît aucun paramètre de type, sauf sur les tableaux. Pour cette raison, le code Scala ne peut pas vérifier si un Option est un Option[Int] ou un Option[String] - cette information a été effacée.

Vous pouvez corriger votre code de cette façon, si:

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 _ => 
    } 
    } 
} 

De cette façon, vous n'êtes pas tester ce type de Option est, mais ce que le type de son contenu sont - en supposant qu'il y est contenu. Un None tombera dans le cas par défaut.

+0

Merci! Les autres réponses sont bonnes aussi, y compris la solution suggérée par Kevin. Mais cela semble être la façon la plus élégante de réparer mon code sans trop réécrire. – Jus12

+0

Pouvez-vous suggérer une solution similaire pour les types génériques? comme dans: 'def test [T] = (Alice!? (100," Hello ")) match {case Quelques (t: T) => println (" T reçu "); case _ => println ("quelque chose d'autre reçu")} ' – Jus12

+1

@ Jus12 Cette façon ne fonctionnera pas. Vous devrez obtenir un 'm: Manifest [T]', puis faire quelque chose comme 'case Some (t: T) si m.erasure.isAssignableFrom (t.getClass()) =>'. –

8

Toute information sur les paramètres de type est uniquement disponible à la compilation -time, pas au moment de l'exécution (c'est ce qu'on appelle un effacement de type). Cela signifie qu'à l'exécution, il n'y a pas de différence entre Option[String] et Option[Int], de sorte que tout modèle correspondant au type Option[String], correspondra également Option[Int] car à l'exécution, les deux sont juste Option.

Comme ce n'est presque toujours pas ce que vous voulez, vous recevez un avertissement. La seule façon d'éviter l'avertissement est de ne pas vérifier le type générique de quelque chose lors de l'exécution (ce qui est correct car cela ne fonctionne pas comme vous le souhaitez de toute façon).

Il n'y a aucun moyen de vérifier si un Option est un Option[Int] ou un Option[String] lors de l'exécution (autre que l'inspection du contenu si c'est un Some).

2

Comme déjà dit, vous êtes contre l'effacement ici.

Pour la solution ... Il est normal avec l'acteur Scala de définir des classes de cas pour chaque type de message que vous êtes susceptible d'envoyer:

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 _ => 
    } 
    } 
}