Questi avvisi si verificano a causa della divertente intersezione tra la risoluzione di sovraccarico, la tipizzazione del target e l'inferenza del tipo. Il compilatore pensa un po 'a te e ti avverte perché la maggior parte dei lambda sono scritti senza tipi dichiarati esplicitamente. Ad esempio, si consideri questa chiamata:
test(1, i -> { });
Qual è il tipo di i
? Il compilatore non può dedurlo finché non ha completato la risoluzione di sovraccarico ... ma il valore 1
corrisponde a tutti e quattro i sovraccarichi. Qualsiasi sovraccarico venga scelto influenzerebbe il tipo di target del secondo argomento, che a sua volta influenzerebbe il tipo dedotto per i
. In realtà non c'è abbastanza informazioni qui per il compilatore per decidere quale metodo da chiamare, quindi questa linea sarà effettivamente causare un errore di compilazione:
error: reference to test is ambiguous
both method test(float,Consumer<Float>) in Test and
method test(double,Consumer<Double>) in Test match
(È interessante notare che le menzioni float
e double
sovraccarichi, ma se si commento uno di questi fuori, si ottiene lo stesso errore rispetto alla long
sovraccarico.)
si potrebbe immaginare una politica in cui il compilatore ha completato la risoluzione di sovraccarico utilizzando la regola più specifico, scegliendo così il sovraccarico con l'int
arg. Avrebbe quindi un tipo di bersaglio definito da applicare al lambda. I progettisti del compilatore hanno ritenuto che fosse troppo sottile e che ci sarebbero stati casi in cui i programmatori sarebbero rimasti sorpresi di quale sovraccarico finisse per essere chiamato. Invece di compilare i programmi in un modo forse inaspettato, hanno ritenuto che fosse più sicuro rendere questo un errore e costringere il programmatore a disambiguarlo.
Il compilatore sta emettendo degli avvisi sulle dichiarazioni del metodo per indicare che il codice probabile che un programmatore scriverà per chiamare uno di questi metodi (come mostrato sopra) provocherà un errore in fase di compilazione.
per disambiguare la chiamata, si sarebbe invece dovuto scrivere
test(1, (Integer i) -> { });
o dichiarare qualche altro tipo esplicito per il parametro i
. Un altro modo è aggiungere un cast prima del lambda:
test(1, (Consumer<Integer>)i -> { });
ma questo è discutibilmente peggiore. Probabilmente non vuoi che i chiamanti della tua API debbano lottare con questo genere di cose in ogni punto della chiamata.
Queste avvertenze non si verificano per il caso Supplier
, perché il tipo di un fornitore può essere determinato tramite ragionamento locale, senza alcuna inferenza di tipo.
Probabilmente vorrete ripensare il modo in cui state mettendo questa API insieme.Se vuoi davvero metodi con questi tipi di argomenti, potresti fare bene a rinominare i metodi testInt
, testLong
, ecc. Ed evitare di sovraccaricarli completamente. Si noti che le API Java SE hanno fatto ciò in casi simili, come comparingInt
, comparingLong
e comparingDouble
su Comparator
; e anche mapToInt
, mapToLong
e mapToDouble
su Stream
.
Cosa succede quando si tenta di chiamare le funzioni di avviso? –
Sembra che l'avviso verrebbe emesso solo quando: a. è un'interfaccia funzionale (ad esempio, consumatore, fornitore), e b. qualsiasi metodo dell'interfaccia contiene parametri ... Come test ho creato le mie copie delle interfacce Consumer/Supplier e ho giocato con loro. Purtroppo non ne so abbastanza di Java funzionale per sapere perché un avviso sarebbe prodotto per questo. Correzione – Kai
: il punto A non è corretto, non ha bisogno di essere un'interfaccia funzionale, solo una qualsiasi interfaccia (la classe non produce alcun avviso) con un solo metodo (non predefinito) definito. Inoltre, ho modificato i parametri di test (int, Consumer) in qualcosa di simile a impossibile essere ambiguo come test (Object, Consumer) v.s. test (Map, Consumer) e tuttavia l'avviso è ancora lanciato. In quanto tale, a meno che non si impieghi qualche programmazione davvero stravagante, non penso davvero che dovrai preoccuparti di questo. – Kai