2012-06-19 11 views
9

Supponiamo di avere una gerarchia di classi come segue:Java Generics, come applicare due argomenti di un metodo che estendono una superclasse per avere lo stesso tipo?

class Vehicle; 
class Car extends Vehicle; 
class Plane extends Vehicle; 

Ho una funzione che confronta i due oggetti

public <T extends Vehicle> generateDiff(T original, T copy) 

In fase di compilazione, il suddetto metodo garantisce i due oggetti è Vehicle, ma non posso assicurarmi che i tipi dei due oggetti siano gli stessi.

generateDiff(new Car(), new Car()); //OK 
generateDiff(new Plane(), new Plane()); //OK 
generateDiff(new Car(), new Plane()); //WRONG 

Posso ottenere questo in fase di compilazione utilizzando Generics?

P.s: attualmente, l'ho implementato genererà un'eccezione se lo di due oggetti non è lo stesso. Ma non sono soddisfatto di questo.

Grazie in anticipo.

risposta

8

Sì, è possibile (tipo di)!

Il tipo T sta essendo inferred dagli argomenti, ma è possibile specificare il tipo:

MyClass.<Car>generateDiff(new Car(), new Plane()); // generates a compile error 

Senza la digitazione del metodo, il tipo T viene dedotta per essere la classe più stretta che soddisfa i limiti usati , così per i parametri Car e Plane, il tipo più stretta che funzionerà è Vehicle, così queste due linee sono equivalenti:

generateDiff(new Car(), new Plane()); // type is inferred as Vehicle 
MyClass.<Vehicle>generateDiff(new Car(), new Plane()); 

Il codice precedente presuppone che lo generateDiff() sia un metodo statico. Se si tratta di un metodo di istanza, è possibile digitare la classe e utilizzare quel tipo nel metodo.

+0

+1 per inferire il tipo! – UmNyobe

3

Non è possibile a mio parere, qualsiasi metodo che possa accettare il veicolo come argomento sarà in grado di accettare CAR e AEREO. Dato che il compilatore non sa che tipo di Oggetti arriverà (può essere CAR, BUS, PLANE), non può garantire che due parametri siano dello stesso tipo. Se qualcuno estende CAR e crea un FORD? entrambi gli oggetti sono di tipo CAR.

Unico modo per garantire che questo sia in fase di esecuzione utilizzando la logica personalizzata.

1

No, questo non può essere raggiunto con o senza farmaci generici. In sostanza, stai chiedendo se puoi dire al compilatore di violare le regole del polimorfismo.

Anche se è stato definito in modo esplicito un metodo senza farmaci generici, come quella qui sotto, sarebbe comunque accettare qualsiasi paio di classi che si estendono Vehicle.

void generateDiff(Vehicle maybePlane, Vehicle maybeCar) { ... 

C'è uno scenario che è un'eccezione, ma non lo consiglierei. Se si chiama il metodo contro gli oggetti classe final (o qualsiasi classe non estesa) che si estende Vehicle, è possibile sovrascrivere il metodo in modo che corrisponda alla firma di quella classe per ciascun parametro. Ma è necessario definirne esplicitamente ciascuno.

class Vehicle; 
final class Car extends Vehicle; 
final class Plane extends Vehicle; 

void generateDiff(Car car1, Car car2) { ... 
void generateDiff(Plane plane1, Plane plane2) { ... 

generateDiff(new Car(), new Car()); // OK 
generateDiff(new Plane(), new Plane()); // OK 
generateDiff(new Car(), new Plane()); // No matching method 
1

Posso raggiungere questo obiettivo al momento della compilazione usando Generics?

No perché i tipi sono impostate in fase di esecuzione, ma si può semplicemente utilizzare introspezione e un'eccezione se un tipo non valido viene inviato alla funzione, ma se questo è il caso allora forse C'è un difetto nella progettazione , ma questa è solo la mia opinione.

instanceof l'operatore verificherà il tipo sottostante e quindi è possibile generare un'eccezione o eseguire un'operazione più appropriata.

public <T extends Vehicle> generateDiff(T original, T copy) 

poiché si può essere confrontando tipi diversi, forse si dovrebbe non hanno una sola funzione, poiché richiederebbe una discreta quantità di if else può essere più saggio per implementare la funzione corrispondente in ogni classe in modo che possano correttamente confrontare con oggetti del tipo appropriato, anche se sto prendendo delle ipotesi che potrebbero essere sbagliate.

6

Una volta approfondito, diventa un po 'astratto. Dovresti fornire anche il tipo di classe per la funzione (vedi sotto). Se si desidera applicare tale comportamento, si consiglia di scrivere metodi separati che accettano i tipi che si desidera confrontare. In ogni caso:

public <C extends Vehicle> void generateDiff(Class<C> type, C original, C copy); 

E si può usarlo come tale:

generateDiff(Plane.class, new Plane(), new Plane()); // OK 
generateDiff(Car.class, new Car(), new Car()); // OK 
generateDiff(Plane.class, new Plane(), new Car()); // ERROR 
generateDiff(Vehicle.class, new Plane(), new Car()); // OK 

Non capisco perché qualsiasi persona sana di mente vorrebbe fare questo però! :)

+0

+1 per "Non so perché una persona sana di mente vorrebbe farlo! :) ' –

0

No, non è possibile!

Questo è quello che vorrei fare:

class Vehicle; 
class Car extends Vehicle; 
class Plane extends Vehicle; 

class DiffGenerator { 
    public Diff generateDiff(Car original, Car copy) { 
    return generateDiff(original, copy) 
    } 

    public Diff generateDiff(Plane original, Plane copy) { 
    return generateDiff(original, copy) 
    } 

    private Diff generateDiff(Vehicle original, Vehicle copy) { 
    return generatedDiff; 
    } 

} 

notato la metodo privato? private Diff generateDiff(Vehicle original, Vehicle copy)

3

In senso stretto, la risposta alla tua domanda è "no, non è possibile".

Tuttavia, c'è un workaround. Creare un metodo <T extends Vehicle> VehicleDiffer<T> compare(T vehicleA) dove VehicleDiffer<T> ha un metodo ReturnType with(T vehicleB). Ora è possibile effettuare le seguenti chiamate:

compare(new Car()).with(new Car()); // okay 
compare(new Plane()).with(new Plane()); // okay 

Di seguito fallirà:

compare(new Car()).with(new Plane()); // with(Car) can't be called with argument type Plane 
+0

Idea geniale che dico. – Saintali

Problemi correlati