2016-06-15 26 views
5

Ho un pezzo di codice in cui un'interfaccia ha un metodo di ritorno opzionale e alcune delle classi che lo implementano per restituire qualcosa, altri no.Java 8 opzionale aggiungi risultato di ritorno solo se opzionale.isPresent

Nel tentativo di abbracciare questo brillante "killer null", qui è quello che ho provato:

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>) 
}} 

Mi scuso per quanto sciocco questo esempio è.
Come posso controllare il reso appena ricevuto e aggiungerlo solo se presente, usando opzionale?

P.S. c'è qualche utilità reale a Opzionale che è se (X! = null) non potesse fare?

+1

"P.S. C'è qualche utilità reale a Opzionale che è se (X! = Null) non potrebbe fare?" - Sono un grande fan di Optional, ma allo stesso tempo lo considero quasi una forma di commento.Quando vedi qualcosa di facoltativo, dovresti automaticamente * sapere * che potrebbe non esserci; tuttavia, la maggior parte dei valori che * può * contenere null mai * dovrebbe *, quindi non è pratica generale circondare assolutamente ogni de-riferimento con assegni nulli. –

+0

@EdwardPeters Non voglio trasformare questo in una sorta di bagno di sangue, ma sono d'accordo con [questo ragazzo qui] (http://huguesjohnson.com/programming/java/java8optional.html): qual è il punto? Sono d'accordo sull'impressione "Oh, un optional! Meglio essere sicuri che effettivamente esiste", ma penso che avrebbe potuto essere un po 'meglio. – Vale

+2

Quindi, parte del beneficio che vedo è la chiarezza. Ogni volta che viene visualizzato il messaggio 'Opzionale' a cui viene assegnato un valore' nullo', * si sa * che è sbagliato e potrebbe sentirsi giustificato nel risolverlo. Quando vedi null viene assegnato ad un altro valore ... beh, chissà, forse è appropriato. Sento anche che, filosoficamente, "Cosa che potrebbe essere nulla" dovrebbe essere davvero un tipo diverso da "Cosa che deve essere qualcosa". Potrebbe essere fatto meglio, ma solo se fosse parte del linguaggio da zero ... e, IMO, Java è composto da una parte di maiale e tre parti di rossetto. –

risposta

5

Vedo dove stai andando con questo - quando un proiettile (può essere un nome di classe migliore di Bullet) passa attraverso BallisticGelPuddy, o si blocca o non lo è. Se si blocca, si accumula in BallisticGelPuddy.

Riscriviamo il codice se stessimo usando null controlli invece:

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

piuttosto semplice, giusto? Se esiste vogliamo aggiungere in

Aggiungiamo lo stile opzionale nel:.

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

efficacemente queste due cose realizzare la stessa cosa, anche se l'approccio è Optional terser.

In questo scenario, non c'è davvero alcuna differenza tra i due approcci dato che si verificherà sempre l'esistenza. Optional è progettato per evitare errori durante la gestione di null e consente di utilizzare express a more fluid call chain, ma considerare la praticità di utilizzare Optional in questo scenario. Non sembra interamente necessario per questo caso.

6

Penso che si desidera:

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

Oppure si può rinunciare alla (codificato) loop di troppo:

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

Ma è più brutto.

3

Con l'API di flusso, si può fare:

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

Purtroppo, in Java 8, non esiste un metodo che converte Optional a Stream, quindi è necessario scrivere voi stessi. Vedi Using Java 8's Optional with Stream::flatMap

0

Voglio postare questo per riferimento futuro, per chiunque inciampi in un problema simile al mio. Se si desidera accedere a metodi di ciò che si è appena restituito, se presente:

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);}); 
}}