Dal Oracle method references tutorial:
riferimento ad un metodo di istanza di un oggetto arbitrario di un particolare tipo
Il seguente è un esempio di un riferimento a un metodo di un oggetto arbitrario di esempio un tipo particolare:
String[] stringArray = { "Barbara", "James", "Mary", "John", "Patricia", "Robert", "Michael", "Linda" };
Arrays.sort(stringArray, String::compareToIgnoreCase);
L'espressione lambda equivalente per il metodo di riferimento String::compareToIgnoreCase
avrebbe l'elenco di parametri formale (String a, String b)
, dove aeb sono nomi arbitrari utilizzati per descrivere meglio questo esempio. Il riferimento al metodo invocerebbe il metodo a.compareToIgnoreCase(b)
.
Ma cosa significa realmente l'operatore ::
? Ebbene, l'operatore ::
può essere descritto come questo (da this SO question):
Metodo di riferimento può essere ottenuta in diversi stili, ma tutti lo stesso significato:
- Procedimento statico (
ClassName::methodName
)
- un metodo di un oggetto particolare istanza (
instanceRef::methodName
)
- procedimento eccellente di un particolare oggetto (
super::methodName
)
- Un metodo di un oggetto arbitrario di un tipo particolare istanza (
ClassName::methodName
)
- Un riferimento costruttore di classe (
ClassName::new
)
- Un riferimento costruzione Array (
TypeName[]::new
)
Quindi, che significa che il metodo il riferimento String::compareToIgnoreCase
rientra nella seconda categoria (instanceRef::methodName
), il che significa che può essere tradotto in (a, b) -> a.compareToIgnoreCase(b)
.
Credo che i seguenti esempi lo illustrino ulteriormente. A Comparator<String>
contiene un metodo che opera su due operandi String
e restituisce uno int
. Questo può essere pseudo descritto come (a, b) ==> return int
(dove gli operandi sono a
e b
). Se si visualizzano in questo modo tutti i seguenti rientra nell'ambito di tale categoria:
// Trad anonymous inner class
// Operands: o1 and o2. Return value: int
Comparator<String> cTrad = new Comparator<String>() {
@Override
public int compare(final String o1, final String o2) {
return o1.compareToIgnoreCase(o2);
}
};
// Lambda-style
// Operands: o1 and o2. Return value: int
Comparator<String> cLambda = (o1, o2) -> o1.compareToIgnoreCase(o2);
// Method-reference à la bullet #2 above.
// The invokation can be translated to the two operands and the return value of type int.
// The first operand is the string instance, the second operand is the method-parameter to
// to the method compareToIgnoreCase and the return value is obviously an int. This means that it
// can be translated to "instanceRef::methodName".
Comparator<String> cMethodRef = String::compareToIgnoreCase;
This great SO-answer to explains how lambda functions are compiled. In questa risposta, Jarandinor si riferisce al seguente passaggio del documento eccellente di Brian Goetz che descrive more about lambda translations.
Invece di generare bytecode per creare l'oggetto che implementa l'espressione lambda (ad esempio chiamando un costruttore per una classe interna), si descrive una ricetta per costruire il lambda, e delegare la costruzione effettiva al runtime linguaggio. Quella ricetta è codificata negli elenchi di argomenti statici e dinamici di un'istruzione invokeynamic.
Fondamentalmente ciò significa che il runtime nativo decide come tradurre il lambda.
Brian continua:
riferimenti metodo sono trattati allo stesso modo di espressioni lambda, se non che la maggior parte dei riferimenti metodo non devono essere private di zucchero in un nuovo metodo; possiamo semplicemente caricare un handle di metodo costante per il metodo di riferimento e passarlo al metafactory.
Quindi, lambda sono private degli zuccheri in un nuovo metodo. Per esempio.
class A {
public void foo() {
List<String> list = ...
list.forEach(s -> { System.out.println(s); });
}
}
Il codice di cui sopra sarà private degli zuccheri a qualcosa di simile:
class A {
public void foo() {
List<String> list = ...
list.forEach([lambda for lambda$1 as Consumer]);
}
static void lambda$1(String s) {
System.out.println(s);
}
}
Ma, Brian spiega anche questo nel documento:
se il metodo Dezuccherato è un'istanza metodo, il ricevitore è considerato il primo argomento
Brian continua a spiegare che gli argomenti rimanenti di lambda vengono passati come argomenti al metodo indicato.
Così, con l'aiuto di this entry by Moandji Ezana, il Dezuccheraggio di compareToIgnoreCase
come Comparator<String>
può essere suddiviso nelle seguenti fasi:
Collections#sort
per un List<String>
aspetta una Comparator<String>
Comparator<String>
è un'interfaccia funzionale con il metodo int sort(String, String)
, che equivale a BiFunction<String, String, Integer>
- L'istanza di confronto può quindi essere fornita da un
BiFunction
lambda compatibile: (String a, String b) -> a.compareToIgnoreCase(b)
String::compareToIgnoreCase
si riferisce ad un metodo di istanza che prende un argomento String
, quindi è compatibile con il lambda sopra: String a
diventa il ricevitore e String b
diventa l'argomento metodo
Edit: dopo l'input dal PO ho aggiunto un basso livello esempio che spiega la Dezuccheraggio
domanda interessante, forse anche più chiaro esempio: 'comparatore super String> cmp = String :: compareToIgnoreCase; "Onestamente, non ho idea di come funzioni. – MightyPork
Credo che questo possa essere chiuso come duplicato di http://stackoverflow.com/questions/20001427/double-colon-operator-in-java-8 – MightyPork