2013-05-20 13 views
5

Il seguente codice compila e funziona correttamente. Ho definito un'interfaccia Builder, quindi la classe CarBuilder è utilizzata per gestire tutto ciò che riguarda un'automobile, e la classe BusBuilder è usata per gestire qualsiasi cosa relativa ad un Bus. Auto e autobus condividono una classe astratta chiamata Veicolo. Il codice è semplice. L'output di codice volontà:Possiamo invocare dinamicamente un metodo di interfaccia Java con un parametro generico?

do something to CREATE the Car 
do something to UPDATE the Car 
do something to CREATE the Bus 
do something to UPDATE the Bus 

Ecco il codice originale che compila:

public abstract class Vehicle { } 
public class Car extends Vehicle { } 
public class Bus extends Vehicle { } 

public interface Builder<V extends Vehicle> { 
    public V createVehicle(String spec); 
    public void updateVehicle(String spec, Vehicle vehicle); 
} 

public class CarBuilder implements Builder<Car> { 
    public Car createVehicle(String spec) { 
     Car car = new Car(); 
     System.out.println("do something to CREATE the Car"); 
     return car; 
    } 
    public void updateVehicle(String spec, Vehicle vehicle) { 
     Car car = (Car) vehicle; 
     System.out.println("do something to UPDATE the Car"); 
     return; 
    } 
} 

public class BusBuilder implements Builder<Bus> { 
    public Bus createVehicle(String spec) { 
     Bus bus = new Bus(); 
     System.out.println("do something to CREATE the Bus"); 
     return bus; 
    } 
    public void updateVehicle(String spec, Vehicle vehicle) { 
     Bus bus = (Bus) vehicle; 
     System.out.println("do something to UPDATE the Bus"); 
     return; 
    } 
} 

@Test 
public void main() { 
    Builder<? extends Vehicle> builder = null; 
    Vehicle vehicle = null; 

    builder = new CarBuilder(); 
    vehicle = builder.createVehicle("my original Car spec"); 
    builder.updateVehicle("my modified Car spec", vehicle); 

    builder = new BusBuilder(); 
    vehicle = builder.createVehicle("my original Bus spec"); 
    builder.updateVehicle("my modified Bus spec", vehicle); 
} 

Ma, mi piacerebbe fare il mio codice ancora più fortemente tipizzato. Voglio cambiare il metodo di interfaccia,

da

public void updateVehicle(String spec, Vehicle vehicle); 

a

public void updateVehicle(String spec, V vehicle); 

In altre parole, ho cercato di usare generica V posto di classe base Vehicle nella mia firma interfaccia. Ciò impone agli implementatori di Builder di "chiudere" il tipo di prodotto specifico (ad esempio Car o Bus, ma non la classe di base Vehicle). CarBuilder deve gestire solo un Car; BusBuilder dovrebbe gestire solo un Bus.

Il codice diventa come segue:

public interface Builder<V extends Vehicle> { 
    public V createVehicle(String spec); 
    public void updateVehicle(String spec, V vehicle); 
} 

public class CarBuilder implements Builder<Car> { 
    public Car createVehicle(String spec) { 
     Car car = new Car(); 
     System.out.println("do something to CREATE the Car"); 
     return car; 
    } 
    public void updateVehicle(String spec, Car car) { 
     System.out.println("do something to UPDATE the Car"); 
     return; 
    } 
} 

public class BusBuilder implements Builder<Bus> { 
    public Bus createVehicle(String spec) { 
     Bus bus = new Bus(); 
     System.out.println("do something to CREATE the Bus"); 
     return bus; 
    } 
    public void updateVehicle(String spec, Bus bus) { 
     System.out.println("do something to UPDATE the Bus"); 
     return; 
    } 
} 

@Test 
public void main() { 
    Builder<? extends Vehicle> builder = null; 
    Vehicle vehicle = null; 

    builder = new CarBuilder(); 
    vehicle = builder.createVehicle("my original Car spec"); 
    builder.updateVehicle("my modified Car spec", vehicle); <== compilation error 

    builder = new BusBuilder(); 
    vehicle = builder.createVehicle("my original Bus spec"); 
    builder.updateVehicle("my modified Bus spec", vehicle); <== compilation error 
} 

Due errori di compilazione - compilatore Java non permetterà questo perché "Il metodo updateVehicle (String, cattura # 3 di estende Vehicle?) Nel tipo Builder non è applicabile per gli argomenti (String, Vehicle) ".

In Java c'è un modo per fare ciò che voglio realizzare? In C# posso semplicemente digitare la mia variabile con var parola chiave, come var vehicle anziché Vehicle vehicle. Ho sentito che l'invocazione generica di Java è una decisione in fase di compilazione. C'è una tecnica per superare questo?

+0

Perché il metodo di aggiornamento di un autobus o di un'auto non dovrebbe essere in quella classe? – thatidiotguy

+0

Ha senso sì. Un Builder è più comunemente usato per creare un'istanza, non per aggiornarlo. – vptheron

risposta

4

C'è un idioma, chiamato a capture helper, che aiuta in casi come questo.

In sostanza, data la vostra dichiarazione di builder, tutto il compilatore sa è che vehicle è qualcosa che si estende Vehicle.Inoltre, sa che Builder richiede un determinato tipo di Vehicle passato al suo metodo updateVehicle(). Il compilatore non è in grado di tracciare l'inferenza che è ovvio per noi — che il tipo è compatibile. Per fare ciò, dobbiamo eliminare il carattere jolly usando un metodo di supporto.

Un'applicazione pratica al codice reale non è ovvia dal semplice cablaggio di prova. Ma applicandolo in questo contesto sembrerebbe qualcosa di simile al seguente. Speriamo che questo esempio sia sufficiente per illustrare l'idea e aiutarti ad applicarla al codice reale.

public void main() 
{ 
    Builder<? extends Vehicle> builder; 
    Vehicle vehicle; 

    builder = new CarBuilder(); 
    vehicle = createAndUpdateHelper(builder, "my original Car spec", "my modified Car spec"); 

    builder = new BusBuilder(); 
    vehicle = createAndUpdateHelper(builder, "my original Bus spec", "my modified Bus spec"); 
} 

private static <V extends Vehicle> V createAndUpdateHelper(Builder<V> builder, String s1, String s2) 
{ 
    V v = builder.createVehicle(s1); 
    builder.updateVehicle(s2, v); 
    return v; 
} 
2

Quello che stai cercando di fare non ha alcun senso: vuoi che i tuoi costruttori accetti solo un'automobile o un autobus (che va bene), ma poi dai loro un'istanza di Veicolo. Stai facendo esattamente la cosa che stai cercando di impedire: accetta la classe generica Veicolo.

+0

Ho dovuto digitare la mia variabile con 'Vehicle' perché non esiste un altro modo per farlo in Java. Come ho accennato in C#, posso scriverlo con 'var' che conserva il tipo di runtime - la mia variabile' vehicle' in runtime contiene una Car o un Bus. Immagino che dipenda dal fatto che la decisione di invocazione generica di Java è stata progettata per essere fatta in fase di compilazione, quindi non c'è altra possibilità per aggirarla. –

+0

Bene, considerando che stai facendo un nuovo CarBuilder() alla linea i e poi chiamalo per creare un veicolo alla linea i + 1 puoi tranquillamente dare il tipo Car al tuo Veicolo appena creato. – vptheron

+0

Nel mio vero progetto, l'istanza Builder viene iniettata, quindi dopo chiamo create() non posso scriverlo su Car o Bus. –

0

Il metodo più semplice sarebbe quello di filtrare l'input che non corrisponde all'ingresso previsto o desiderato.

if(vehicle instanceof Car) return; 

Una più approfondita (e meno fragile) implementazione può essere creato utilizzando le enumerazioni per controllare vari aspetti della vettura (tipo, colore). È quindi possibile utilizzare un singolo generatore con interruttori per gestire qualsiasi combinazione di questi. (Ho scritto un codice di esempio per questo, se ne avete bisogno, sentitevi liberi di mandarmi un messaggio e io vi invierò una copia)

+0

Funzionerebbe sì, ma il suo intento è quello di avere un codice fortemente tipizzato, non ricevere errori in fase di esecuzione (invece di restituire vorrei lanciare un IllegalArgumentException o qualcosa del genere). – vptheron

+0

Ben detto vtheron. Grazie per la tua risposta. –

+0

@vtheron Sono completamente d'accordo, ma se la semplicità fosse un obiettivo, questo è il modo più semplice per risolvere potenziali problemi. Dammi un momento che sto scrivendo il modo migliore (secondo me) per gestirlo ora. –

Problemi correlati