2014-09-16 5 views
5

Ho seguente codice:Qualcuno può raccomandare un modello java 8 per sostituire un'istruzione switch?

public class A { 
    private String type; 
    String getType() { return type;} 
} 

Ora in molti luoghi codice che ho il codice come questo

switch (a.geType()) { 
    case "A" : return new Bla(); 
    case "B" : return new Cop(); 
} 

o da qualche altra parte

switch (a.geType()) { 
    case "A" : return new Coda(); 
    case "B" : return new Man(); 
} 

(Si noti che So che dovrei usare un Enumerazione nel codice di produzione).

Quello che voglio ottenere è che quando un nuovo tipo viene aggiunto alla classe A il compilatore deve contrassegnare tutte le istruzioni switch che devono essere regolate?

C'è un modo idiomatico java per fare questo?

+3

Se si utilizza un enum, sono abbastanza sicuro che java in realtà contrassegna le istruzioni switch che non gestiscono tale condizione, a condizione che lo switch non abbia una condizione 'default'. – Pokechu22

+0

L'ho appena provato. Non contrassegna le enumerazioni mancanti fuori dalla scatola – jack

+1

Durante l'operazione ricevo un avviso in eclissi. Tuttavia, potrebbe non essere parte del compilatore java stesso. – Pokechu22

risposta

11

quando viene aggiunto un nuovo tipo di class A il compilatore dovrebbe bandiera tutte le switch dichiarazioni che hanno bisogno di essere regolata?

Un buon approccio a questo sarebbe sostituendo switch dichiarazioni con una più robusta implementazione di spedizione multipla, come ad esempio il Visitor Pattern:

interface VisitorOfA { 
    Object visitA(A a); 
    Object visitB(B b); 
} 
class A { 
    Object accept(VisitorOfA visitor) { 
     return visitor.visitA(this); 
    } 
} 
class B extends A { 
    Object accept(VisitorOfA visitor) { 
     return visitor.visitB(this); 
    } 
} 

Con questa infrastruttura in atto, è possibile rimuovere il tuo switch dichiarazioni, la loro sostituzione con le implementazioni del visitatore:

Object res = a.accept(new VisitorOfA() { 
    public Object visitA(A a) { return new Bla(); } 
    public Object visitB(B b) { return new Cop(); } 
}); 

quando si aggiunge un nuovo sottotipo di A, diciamo, class C, tutto quello che dovete fare è l'aggiunta di un nuovo metodo per VisitorOfA:

Object visitC(C c); 

Ora il compilatore individuare tutti i luoghi in cui non è stato implementato questo nuovo metodo, aiutandoti a evitare problemi in fase di esecuzione.

+0

In qualche modo le istruzioni switch sembrano più leggere e quindi più facili da capire per chi legge il codice. Ma devo ammettere che la tua risposta risolve il requisito "flaming del compilatore" – jack

+0

Un buon miglioramento per questo approccio sarebbe quello di memorizzare un'istanza di ogni implementacion in una Map . Inoltre, è possibile caricare la mappa da un file .properties. –

+0

@jack Sono d'accordo, il modello di visitatore sembra complicato per qualcuno che lo guarda per la prima volta. Sono molto prevenuto qui, perché ho usato questo modello per molti anni. Il vantaggio, tuttavia, è che uno deve imparare questo modello solo una volta per riconoscerlo in modo affidabile in futuro. È come imparare ad andare in bicicletta :) – dasblinkenlight

2

Si potrebbe avere una mappa di stringa/fornitori:

Map<String, Supplier<Object>> map = new HAshMap<>(); 
map.put("A", Bla::new); 
map.put("B", Cop::new); 

e il codice di esempio sarebbe diventato:

return map.get(a.getType()).get(); //need null check 
7

Non dimenticare buon polimorfismo all'antica. Avere un campo "tipo" con istruzioni switch in una classe è spesso un odore che indica che la sottoclasse potrebbe essere utile. Si consideri:

public abstract class CommonSuperClass { 
    public abstract One getOne(); 
    public abstract Two getTwo(); 
} 

public class A extends CommonSuperClass { 
    @Override public One getOne() { return new Bla(); } 
    @Override public Two getTwo() { return new Coda(); } 
} 

public class B extends CommonSuperClass { 
    @Override public One getOne() { return new Cop(); } 
    @Override public Two getTwo() { return new Man(); } 
} 

Se si dovesse aggiungere una nuova sottoclasse C, ti viene richiesto di fornire implementazioni per i metodi astratti (a meno che non si effettua C per sé un abstract).

0

In prospettiva dell'astrazione, c'è un altro approccio da utilizzare. Un modo è via Polymorphism as shown here.

Qualche semplice esempio:

public void EverythingYouWant (Animal animal) { 
    return animal.move(); 
} 

Quando è più circa refactoringreplace type code/checking with State/Strategy modelli. È una buona soluzione considerare innanzitutto se esiste una ragione che impedisce la sottoclasse.