Je construis un simulateur qui analyse certains événements de STDIN
et "les explose". Mon origine est principalement une programmation fonctionnelle ces jours-ci, il semblait donc naturel de faire quelque chose comme ceci:
data Event = Thing1 String Int | Thing2 Int | Thing3 String String Int
Parse :: String -> [Event]
Simulate :: [Event] -> [Result]
où simuler serait
case event
of Thing1 a b => compute for thing one
| Thing2 a => compute for thing two
etc. Quel est le moyen idiomatique de faire ce genre de chose en Java? Googling m'a signalé dans la direction de classes imbriquées et à la tendance des visiteurs, mais cela semble plutôt lourd dans ma tentative. Type Erasure semble me combattre, dur. Pourriez-vous me montrer un aperçu de ce que cela ressemblerait correctement?
L'auteur de 'Programming fonctionnelle à Scala' donne une belle illustration du meilleur qui peut être atteint dans Java de type sécurisé:
http://blog.higher-order.com/blog/2009/08/21/tructural-pattern-Matching-in-java/
Essentiellement, il utilise un codage de l'Église des cas afin de garantir que le compilateur se plaindre le cas échéant.
Les détails ne sont pas facilement résumés et sont très bien couverts dans l'article qu'il ne sert à rien de les reproduire ici (c'est ce que les hyperliens sont pour ?) .
Quel est le moyen idiomatique de faire ce genre de chose en Java?
Il n'y a pas vraiment une telle chose, étant donné que Java (la langue) est fondamentalement impératif.
Si vous pouvez exécuter sur la JVM, mais non limité au Java Langue , vous pourriez enquêter sur Scala, qui serait Atteindre quelque chose comme le ci-dessus en utilisant correspondance de motif .
Sinon, je pense que vous êtes réduit de faire correspondre manuellement vos divers cas et de faire appel à des méthodes, le cas échéant, ou peut-être définir des sous-types de "événement" et d'utiliser le polymorphisme pour invoquer des méthodes particulières pour chaque sous-type.
Motif des visiteurs ou son équivalent de l'église est la voie à suivre. C'est plutôt verbeux dans Java, mais j'espère que des outils comme -- DERIVE4J (un processeur d'annotation que je maintienne) ou ADT4J peut générer la plaque chauffante. Utilisation d'un tel outil, votre exemple devient:
import Java.util.function.Function;
import org.derive4j.Data;
@Data
public abstract class Event {
interface Cases<X> {
X Thing1(String s, int i);
X Thing2(int i);
X Thing3(String s, String s2, int i);
}
abstract <X> X match(Cases<X> cases);
static Function<Event, Result> Simulate =
Events.cases().
Thing1( (s, i ) -> computeForThingOne(s, i) ).
Thing2( (i ) -> computeForThingTwo(i) ).
Thing3( (s, s2, i) -> computeForThingThree(s, s2, i) );
}
DERIVE4J Générez la classe d'événements qui fournissent une syntaxe de correspondance de motif fluide (avec vérification exhaustive que tous les cas sont traités).
Vous pouvez utiliser une énumération et une interface, remplacer la méthode simulée, comme ceci:
interface Event {
void simulate()
}
enum MyEvents implements Event {
THING1 {
@Override
void simulate() {
//...
}
},
THING2 {
@Override
void simulate() {
//...
}
},
}
Disons que vous avez Event event
. Ensuite, vous pouvez l'utiliser de deux manières:
event.simulate();
ou
switch(event) {
case THING1:
//..
break;
case THING2:
break;
}
Mais le constructeur est appelé automatiquement par le JVM, afin de stocker également les paramètres, vous allez devoir ajouter des propriétés avec des accesseurs, etc.
Sinon, vous pouvez coder vos événements comme chaînes constantes et utiliser la construction switch
, auquel cas vous feriez
string event = args[0];
switch(event){
case THING1:
int a = args[1];
//...
break;
case THING2:
int a = args[1];
int b = args[2];
break;
}
etc. Mais oui, il n'y a rien d'original qui imite directement le motif de motif :(