Sei sulla pagina 1di 2

Controllo semantico sui metodi

di Michele Arpaia

Descrizione del problema


Come è noto in Java (così come in C/C++) l’overloading di un metodo è possibile a patto che la lista dei
parametri formali sia di tipo differente. Ci sono casi in cui due metodi accettano la stessa lista di parametri
(dello stesso tipo) ma hanno un significato diverso. In tal caso il compilatore lancia un errore poiché questo si
limita ad un controllo sintattico. Il motivo di tal comportamento è che l’interprete a run-time non riuscirebbe a
disambiguare i metodi trovandosi nell’incapacità di quale metodo scegliere.

Soluzione al problema
Lo spunto mi è dato da un risposta di Carlo Pescio ad un lettore sul numero 62 di Computer Programming. In
quell’occasione si presentava un esempio simile al seguente: una classe gestisce operazioni su coordinate
cartesiane e polari. I costruttori assumerebbero la seguente signature:

Cvector (float x, float y, float z) // coordinate cartesiane


Cvector(float rho, float theta, float phi) // coordinate polari

Ovviamente il compilatore lancia un errore di ridefinizione. Una semplice soluzione sarebbe quella di avere
un solo costruttore con un parametro booleano in più, e capire con degli if quale significato deve assumere il
costruttore. Un siffatta soluzione offre il fianco a molte limitazioni. Intanto, da un punto di vista Object
Oriented, il metodo ha una low cohesion ovvero ha molte responsabilità, questo diventa difficile da
manutenere, da comprendere e da riusare. Basti pensare all’introduzione di un altro tipo di coordinate, come
quelle cilindriche. In base a questa soluzione, bisogna aggiungere un altro if, rimuovere il flag poiché
essendo booleano assume solo due valori, e sostituirlo con un byte, ma questo potrebbe assumere valori
che da un punto di vista semantico non hanno nessun significato, e così via.
Pescio in quella risposta utilizza i tipi enumerativi che in Java non sono presenti. L’idea è allora quella di
sfruttare la potenza delle inner-class. Vediamo il listato e poi lo commentiamo:

class CVector implements CVector.Coordinate


{
public static interface Coordinate
{
cartesian CARTESIANE = new cartesian();
polar POLARI = new polar();

final class cartesian{}


final class polar{}
}

CVector (float x,float y, float z, Coordinate.cartesian c)


{
System.out.println("cartesian");
}
CVector (float rho, float theta, float phi, Coordinate.polar p)
{
System.out.println("polar");
}

public static void main(String[] arg)


{
CVector v1= new CVector (3f,2f, 0.4f CVector.CARTESIANE);
CVector v2= new CVector (2f,1.2f,20.4f CVector.POLARI);
}
}
la classe CVector implementa una inner-interface che definisce i tipi di variabili (classi vuote) che
consentono di disambiguare i metodi (saranno i parametri formali dei metodi) e costanti di quel tipo che
fungeranno da parametri attuali quando si invocheranno quei metodi (che nel nostro caso sono costruttori).
A questo punto tutto è risolto. Volendo contemplare anche le coordinate cilindriche basta aggiungere una
costante e una classe vuota nella inner-interface e fornire un ulteriore metodo che tra i suoi parametri formali
abbia il nuovo tipo. Questa soluzione mantiene alto la coesione dei metodi con tutti i vantaggi che ne
conseguono.
Un ultima nota riguarda la performance. Riprendendo la risposta di Pescio, “la chiamata al costruttore
corretto viene risolta a tempo di compilazione, evitando l’esecuzione di un test e di un salto condizionale a
run-time, quindi il codice generato è più veloce”.