web-dev-qa-db-fra.com

Java 8 facultatif ajoute le résultat renvoyé uniquement s'il est facultatif.isPresent

J'ai un élément de code dans lequel une interface a une méthode de retour facultatif et certaines des classes qui l'implémentent pour renvoyer quelque chose, d'autres non.

Dans le but d’embrasser ce brillant "null killer", voici ce que j’ai essayé:

public interface Gun {
    public Optional<Bullet> shoot();
}

public class Pistol implements Gun{
    @Override
    public Optional<Bullet> shoot(){
        return Optional.of(this.magazine.remove(0)); 
    }//never mind the check of magazine content
}

public class Bow implements Gun{
    @Override
    public Optional<Bullet> shoot(){
        quill--;
        return Optional.empty();
    }
}

public class BallisticGelPuddy{
    private Gun[] guns = new Gun[]{new Pistol(),new Bow()};
    private List<Bullet> bullets = new ArrayList<>();
    public void collectBullets(){
        //here is the problem
        for(Gun gun : guns)
            gun.shoot.ifPresent(bullets.add( <the return I got with the method>)
}}

Je m'excuse pour la bêtise de cet exemple.
Comment puis-je vérifier le retour que je viens de recevoir et l’ajouter uniquement si présent, en utilisant facultatif?

P.S. Existe-t-il une utilité réelle pour Optional, qui est si (X! = null) ne pouvait pas faire?

6
Vale

Je vois où vous voulez en venir: lorsqu'un projectile (peut-être un meilleur nom de classe que Bullet) traverse BallisticGelPuddy, il reste bloqué ou non. S'il se coince, il s'accumule dans BallisticGelPuddy.

Réécrivons le code si nous utilisions plutôt des contrôles null:

for(Gun gun: guns) {
    final Bullet bullet = gun.shoot();
    if(bullet != null) {
        bullets.add(bullet);
    }
}

Assez simple, non? S'il existe, nous voulons l'ajouter à.

Ajoutons le style optionnel dans:

for(Gun gun: guns) {
    gun.shoot().ifPresent(bullets::add);
}

Effectivement, ces deux choses accomplissent la même chose, bien que l’approche Optional soit floue.

Dans ce scénario, il n'y a vraiment aucune différence entre les deux approches puisque vous allez toujours vérifier l'existence. Optional est destiné à éviter les erreurs lors de la manipulation de null et vous permet de exprimer une chaîne d'appels plus fluide , mais considérez le caractère pratique de l'utilisation de Optional dans ce scénario. Cela ne semble pas entièrement nécessaire pour ce cas.

10
Makoto

Je pense que tu veux:

gun.shoot().ifPresent(bullets::add);

Ou vous pouvez également vous passer de la boucle (codée):

guns.stream()
  .map(Gun::shoot)
  .filter(Optional::isPresent)
  .map(Optional::get)
  .forEach(bullets::add);

Mais c'est plus laid.

9
Bohemian

Avec l'API de flux, vous pouvez faire:

    List<Bullet> bullets = Arrays.stream(guns)
            .map(Gun::shoot)
            .flatMap(this::streamopt) // make Stream from Optional!
            .collect(Collectors.toList());

Malheureusement, en Java 8, aucune méthode ne permet de convertir Optionals en Stream, vous devez donc l'écrire vous-même. Voir Utilisation de Java 8 avec Optional avec Stream :: flatMap

3
user140547

Je souhaite poster ceci pour référence future, pour tous ceux qui s’affrontent dans un problème similaire au mien. Si vous souhaitez accéder aux méthodes de ce que vous venez de retourner, le cas échéant:

public class Bullet{
    private int weight = 5;
    public int getWeight(){ return weigth;}
}
public interface Gun {
    public Optional<Bullet> shoot();
}

public class Pistol implements Gun{
    @Override
    public Optional<Bullet> shoot(){
        return Optional.of(this.magazine.remove(0)); 
    }//never mind the check of magazine content
}

public class Bow implements Gun{
    @Override
    public Optional<Bullet> shoot(){
        quill--;
        return Optional.empty();
    }
}

public class BallisticGelPuddy{
    private Gun[] guns = new Gun[]{new Pistol(),new Bow()};
    private List<Bullet> bullets = new ArrayList<>();
    private int totWeigth = 0;
    public void collectBullets(){
        // IF YOU WANT TO ONLY ADD WHAT YOU HAVE FOUND IN A COMPATIBLE CLASS
        // thanks to makoto and bohemian for the answers
        for(Gun gun : guns)
            gun.shoot.ifPresent(bullets::add)
        //IF YOU WANT TO ACCESS THE RETURNED OBJECT AND ADD IT TOO
        for(Gun gun : guns)
            gun.shoot.ifPresent( arg -> {totWeight += arg.getWeigth();
                                         bullets.add(arg);});
}}
0
Vale