37 votes

Le moyen le plus court d'itérer sur une non-liste ?

Supposons que j'ai 3 instances de Scanner que je veux fermer.

Je pourrais faire

sc.close()

pour chacun des scanners.

Ou je pourrais faire quelque chose comme

for (Scanner sc: new Scanner[]{sc1,sc2,sc3}) {
    sc.close();
}

Existe-t-il un moyen plus court de faire cela avec Java 8 ?

Quelque chose de similaire à ?

{sc1,sc2,sc3}.forEach((sc) -> sc.close());

1 votes

10 votes

Pour répondre à la question générale : Stream.of(sc1, sc2, sc3).forEach(Scanner::close); mais pour ce cas spécifique écouter Gerald Mücke

75voto

Gerald Mücke Points 6391

Depuis Java 7, vous devriez utiliser try-with-resources

try(Scanner sc1 = new Scanner(""); 
    Scanner sc2 = new Scanner(""); 
    Scanner sc3 = new Scanner("")){

}
//all scanners get closed implicitly

Vous n'avez donc pas besoin de code du tout.

Le problème avec toutes les constructions for-each ou stream est que - théoriquement - si le premier close() échoue avec une exception lors de l'invocation de la source sous-jacente. close() les scanners suivants ne seront pas fermés. Le site Scanner.close() attrape toute exception IOException, mais aucune autre exception qui pourrait se produire.

La construction try-with-resources s'en occupe, les boucles ne le font pas.


EDITAR : Alors que votre question visait une approche plus générale, la solution ci-dessus était une réponse à votre problème particulier : faire face à AutoCloseable qui, en tout état de cause, doivent être utilisées avec l'outil de gestion des ressources. essayer avec les ressources qui ne nécessite aucun traitement particulier de la méthode de fermeture (= solution la plus courte pour votre problème particulier).

En ce qui concerne la question plus générale du traitement des éléments arbitraires (qui ne sont pas des ressources), Java a au moins deux options :

Création d'une liste à partir d'un tableau/de variables et itération sur cette liste

for(YourItemType item : Arrays.asList(your,items,here)) {
  //do something
}

Créer un flux à partir d'un tableau/Varargues et lui appliquer des fonctions

Stream.of(your,items,here).forEach(item -> { doSomething});

Bien sûr, le "doSomething" peut être remplacé par une référence de méthode

Stream.of(your,items,here).forEach(this::myMethod);
...
void myMethod(YourItemType item){
  //doSomething
} 

Le problème avec cette approche est que les exceptions vérifiées doivent être traitées explicitement dans les expressions lambda. Prenons l'exemple ci-dessus et laissons myMethod lancer une exception vérifiée

void myMethod(YourItemType item) throws Exception

dans ce cas, votre déclaration de flux devrait ressembler à ceci

Stream.of(your,items,here).forEach(item -> {
  try {
    myMethod(item);
  } catch (Exception e){
    //omit or throw new RuntimeException(e);
  };

Ça n'a pas l'air si bien que ça. Mais nous pourrions mettre le corps lambda dans une méthode séparée

void myMethodQuietly(YourItemType item) {
  try {
    myMethod(item);
  }catch(Exception e){
    //omit or throw new RuntimeException(e);
  }
}

Stream.of(your,items,here).forEach(this::myMethodQuietly);

Cette approche peut s'avérer intéressante pour votre problème particulier de ressources. Nous pouvons mettre tout cela dans un CompositeAutoCloseable qui prend des ressources créées en dehors de la classe et qui doivent toutes être fermées en toute sécurité lors de l'invocation de la fonction close()

public class CompositeAutoCloseable implements AutoCloseable {

  private List<Closeable> resources;

  public CompositeAutoCloseable(Closeable... resources) {
    this.resources = Arrays.asList(resources);
    //you could use a stream here too
  }

  @Override
  public void close() {
      this.resources.stream().forEach(this::closeQuietly);
  }

  void closeQuietly(Closeable res) {
    if(res == null)  {
        return;
    }
    try {
        res.close();
    }catch(Exception e){
        //omit
    }
  } 
}

Et une fois que vous avez une telle classe d'aide, vous pouvez l'utiliser à nouveau avec try-with-resources.

try(CompositeAutoCloseable cac = new CompositeAutoCloseable(sc1,sc2,sc3)) {
  //do something
}

Je vous laisse le soin de décider si cela a du sens par rapport à la solution initiale ;)

1 votes

Cette réponse me semble la plus logique. Tirer parti de AutoCloseable au lieu du hard-streaming pour fermer explicitement le fichier Scanner semble la solution la plus naturelle.

0 votes

Notez que try-with-resource n'est toujours pas sûr à 100%. Voici un exemple simple en C# où cela peut poser problème avec la construction correspondante : stackoverflow.com/a/21123756/1348195

6 votes

@Benjamin Gruenbaum : ce post utilise beaucoup de constructions C# qui n'existent pas en Java. En général, ce n'est pas la responsabilité du code. en utilisant Scanner pour corriger des bugs potentiels dans le constructeur de la classe Scanner . Bien sûr, ce n'est pas "sûr à 100%", mais rien ne l'est. Comment se protéger contre quelqu'un qui débranche la prise ?

3voto

Basilevs Points 4048

Si l'instanciation est séparée de l'élimination, utilisez l'outil de Guava com.google.common.io.Closer .

-1voto

OldCurmudgeon Points 16615

Bien qu'il existe parfois une corrélation entre la longueur du code et sa qualité, ce n'est pas un bon critère à utiliser pour choisir un code.

J'utiliserais probablement des varargues et je ferais quelque chose comme ça :

private void closeScanner(Scanner... scanners) {
    // Faff around with Java 8 in here if you like.
    for (Scanner s : scanners) {
        s.close();
    }
}

    // Some code.
    closeScanner(s, t, u);

0 votes

Défaut de sécurité exceptionnel

0 votes

@basilevs - Scanner.close ne lève pas les exceptions.

0 votes

@OldCurmudgeon : bien, il ne lance que des exceptions non vérifiées.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X