Ogni intervistato finora ha contribuito positivamente, ma cercherò di avvolgere le diverse idee su in una sola risposta. La chiave per capire cosa sta succedendo è come il compilatore Java e la JVM implementano i generici - questo spiega il bridge, e perché è falso nell'interfaccia.
In sintesi, però:
Il metodo più generico int op(Number)
è richiesto per la compatibilità firma e costruire il metodo ponte di attuazione
Procedimento isBridge()
può essere vero solo per le classi di cemento, non interfacce
Probabilmente non importa quale dei due metodi si raccolgono, funzioneranno in modo identico nel runtime.
Ok, ecco la risposta lunga:
implementazione di Java Generics
Quando si dispone di una classe o interfaccia generica, il compilatore costruisce un metodo nel file di classe con ogni tipo generico sostituito con un tipo concreto appropriato. Ad esempio, ITest
ha un metodo:
int op(T value)
cui la classe definisce T
come T extends Number
, in modo che il file di classe ha un metodo:
int op(Number);
Quando si utilizza ITest
, il compilatore crea classi aggiuntive per ogni digitare il generico è risolto a.Ad esempio, se c'è una riga di codice:
ITest<Double> t = new ...
Il compilatore produce una classe con i seguenti metodi:
int op(Number);
int op(Double);
Ma sicuramente la JVM ha bisogno solo la versione int op(Double)
? Il compilatore non si assicura che ITest<Double>
riceva solo chiamate su op(Double)
?
Ci sono due ragioni per cui Java ha bisogno il metodo int op(Number)
:
orientamento oggetto richiede che ovunque si utilizza una classe si può sempre sostituirlo con una sottoclasse (almeno dal tipo di sicurezza). Se int op(Number)
non esiste, la classe non fornirà un'implementazione completa della firma della superclasse (o super-interfaccia).
Java è un linguaggio dinamico con casting e riflessione, pertanto è possibile chiamare il metodo con un tipo errato con . A questo punto, Java garantisce di ottenere un'eccezione di classe.
Infatti, l'attuazione di 2. viene raggiunto dal compilatore producendo un 'metodo bridge'.
Che cosa fa un metodo bridge?
Quando ITest<Double>
viene creato dal ITest<T extends Number>
, il compilatore crea il metodo int op(Number)
, e la sua implementazione è:
public int op(Number n) {
return this.op((Double) n);
}
Questa implementazione ha due proprietà:
dove N è un Double
, delega la chiamata a int op(Double)
e
Dove n è non a Double
, causa lo ClassCastException
.
Questo metodo è un "ponte" dal tipo generico al tipo di calcestruzzo. Per la sua stessa natura, i metodi di calcestruzzo solo possono essere ponti, quindi int op(Double)
sull'interfaccia secondaria è solo una firma.
E l'OP?
Nell'esempio in questione, il file di classe sub-interfaccia ITestDouble
creato dal compilatore ha entrambi i metodi:
int op(Number);
int op(Double);
Il int op(Number)
è necessario in modo che le implementazioni di ITestDouble
possono avere il loro metodo di bridge - ma questo metodo non è di per sé un bridge perché è solo una firma, non un'implementazione.Probabilmente Sun/Oracle ha mancato un trucco qui, e potrebbe valere la pena sollevare un bug con loro.
Come trovare il metodo corretto?
In primo luogo, importa? Tutte le implementazioni di ITestDouble
avranno il metodo bridge inserito automaticamente dal compilatore e il metodo bridge chiama il metodo int op(Double)
. In altre parole, non importa quale metodo venga chiamato, basta sceglierne uno.
In secondo luogo, in fase di esecuzione molto probabilmente passerai le istanze , non le interfacce. Quando esegui lo getMethods()
, sarai in grado di distinguere tra il metodo bridge e l'effettiva implementazione. Questo è ciò che ha detto johncarl.
In terzo luogo, se è necessario risolvere questo problema interrogando l'interfaccia, è possibile testare gli argomenti per il sottotipo "più basso". Ad esempio, in un meta-livello:
Raccogliere tutti i due metodi con lo stesso nome
Raccogliere il tipo di argomento: Method.getParameterTypes()[0]
Usa Class.isAssignableFrom(Class)
. Il metodo restituisce true se l'argomento è lo stesso o una sottoclasse della classe su cui viene chiamato il metodo.
Utilizzare il metodo con l'argomento sottoclasse dell'argomento del metodo diverso.
Si vedono solo ponti su classi e non interfacce poiché il bridge è un pezzo di codice di stub in cui un metodo ne chiama un altro. –
Infatti, come faccio a capire che in realtà esiste un solo metodo 'op'? – Flavio
Sì, ma non conosco il modo semplice per dire che sono uguali solo dalle interfacce. Puoi guardare le informazioni generiche per ITest e dedurre che i metodi sono gli stessi ma è molto lavoro. –