2015-04-19 12 views
8

Ho letto il tutorial Java 8 su Espressioni lambda e non comprendo l'esempio metodo di riferimento per "riferimento ad un metodo di un oggetto arbitrario di un particolare tipo di istanza"Java 8 e metodo referenze - specificamente compareToIgnoreCase

In lo stesso tutorial è un esempio di "Riferimento a un metodo di istanza di un oggetto particolare" che sembra simile.

public int compareByName(Person a, Person b) { 
     return a.getName().compareTo(b.getName()); 
} 
Arrays.sort(rosterAsArray, myComparisonProvider::compareByName); 

posso vedere questo lavoro perché il metodo compareByName ha la stessa firma Comparator.compare, lambda (a, b) -> myComparisonProvider.compareByName (a, b) prende due argomenti e chiama un metodo con il stessi due argomenti.

Ora il "riferimento ad un metodo di un oggetto arbitrario di un particolare tipo di istanza" esempio utilizza String :: compareToIgnoreCase

String[] stringArray = { "Barbara", "James", "Mary", "John", 
    "Patricia", "Robert", "Michael", "Linda" }; 
Arrays.sort(stringArray, String::compareToIgnoreCase); 

La firma di tale metodo è int compareTo(String anotherString) ed è diverso Comparator.compare. Il tutorial non è molto chiaro ma sembra implicare che si finisce con un lambda come (a, b) -> a.compareToIgnoreCase (b) Non capisco come il compilatore decida cosa è accettabile per il secondo parametro di Arrays.sort I ho pensato che forse è abbastanza intelligente da capire come chiamare quel metodo, quindi ho creato un esempio.

public class LambdaTest { 

    public static void main(String... args) { 
     String[] stringArray = { "Barbara", "James", "Mary", "John", "Patricia", "Robert", "Michael", "Linda" }; 

     Arrays.sort(stringArray, String::compareToIgnoreCase);  // This works 

     // using Static methods 
     Arrays.sort(stringArray, FakeString::compare);    // This compiles 
     Arrays.sort(stringArray, FakeString::compareToIgnoreCase); // This does not 

     // using Instance methods 
     LambdaTest lt = new LambdaTest(); 
     FakeString2 fs2 = lt.new FakeString2(); 
     Arrays.sort(stringArray, fs2::compare);     // This compiles 
     Arrays.sort(stringArray, fs2::compareToIgnoreCase);  // This does not 

     for(String name : stringArray){ 
      System.out.println(name); 
     } 
    } 

    static class FakeString { 
     public static int compareToIgnoreCase(String a) { 
      return 0; 
     } 


     public static int compare(String a, String b) { 
      return String.CASE_INSENSITIVE_ORDER.compare(a, b); 
     } 
    } 

    class FakeString2 implements Comparator<String> { 
     public int compareToIgnoreCase(String a) { 
      return 0; 
     } 

     @Override 
     public int compare(String a, String b) { 
      return String.CASE_INSENSITIVE_ORDER.compare(a, b); 
     } 
    } 
} 

qualcuno può spiegare perché due precedenti Arrays.sort non si compilano anche se non stanno usando metodi che sono lo stesso come metodo di String.compareToIgnoreCase

+0

prega di fare riferimento [String :: compareToIgnoreCase su StackOverflow] (https://stackoverflow.com/questions/39073799/type-inference-in-method-reference) –

risposta

6

In FakeString, Your compareToIgnoreCase ha un unico String argomento, quindi non può venire al posto di un Comparator<String>, che richiede un metodo con due argomenti String.

In FakeString2, il vostro compareToIgnoreCase ha un FakeString argomento implicito (questo) e un argomento stringa, quindi, ancora una volta, non può venire al posto di un Comparator<String>.

+2

Ma 'String # compareToIgnoreCase' avere anche un solo argomento stringa. –

+4

@ Jean-FrançoisSavard 'String # compareToIgnoreCase', proprio come' String # compareTo' ha un secondo argomento String implicito - questo, che consente di utilizzarlo dove è richiesto un metodo con due argomenti String. – Eran

+0

Penso che se sviluppi l'idea di un argomento 'implicito', otterrai la risposta attesa. Sono anche interessato a leggere che anche se ora riesco a capire perché funziona – Dici

5

Questa è la differenza tra un riferimento al metodo su alcuni oggetti e un riferimento al metodo sull'oggetto elaborato.

primi esempi di Oracle

Vediamo questo primo caso:

public int compareByName(Person a, Person b) { 
     return a.getName().compareTo(b.getName()); 
} 
Arrays.sort(rosterAsArray, myComparisonProvider::compareByName); 

Qui, il metodo compareByName viene chiamato sul passato in istanza di myComparisonProvider con ogni coppia di argomenti nel sort algoritmo.

Quindi, ecco, quando si confrontano a e b abbiamo effettivamente chiamiamo:

final int c = myComparisonProvider.compareByName(a,b); 

Ora, nel secondo caso:

String[] stringArray = { "Barbara", "James", "Mary", "John", 
    "Patricia", "Robert", "Michael", "Linda" }; 
Arrays.sort(stringArray, String::compareToIgnoreCase); 

si ordina un String[] modo che il metodo compareToIgnoreCase viene chiamato sul L'istanza String viene attualmente ordinata con l'altro String come argomento.

Quindi, ecco, quando si confrontano a e b abbiamo effettivamente chiamiamo:

final int c = a.compareToIgnoreCase(b); 

Quindi queste sono due casi diversi:

  • quella in cui si passa in un metodo su un'istanza di un oggetto arbitrario; e
  • uno dove si passa in un metodo per essere chiamato sull'istanza in elaborazione.

sul vostro esempi

Ora nel tuo primo esempio, si hanno anche un String[] e si cerca di risolvere la cosa. Quindi:

Arrays.sort(stringArray, FakeString::compare); 

Quindi, ecco, quando si confrontano a e b abbiamo effettivamente chiamiamo:

final int c = FakeString.compare(a, b); 

L'unica differenza è compare è static.

Arrays.sort(stringArray, FakeString::compareToIgnoreCase); 

Ora, il String[] non è un FakeString[] quindi non possiamo chiamare questo metodo su String. Pertanto, dobbiamo chiamare il metodo static su FakeString. Ma non possiamo farlo neanche perché abbiamo bisogno di un metodo (String, String) -> int ma abbiamo solo (String) -> int - errore di compilazione.

Nel secondo esempio il problema è esattamente lo stesso, poiché si ha ancora un String[]. E compareToIgnoreCase ha la firma sbagliata.

TL; DR:

Il punto vi manca è che nell'esempio String::compareToIgnoreCase; il metodo è chiamato su Stringattualmente in fase di elaborazione.

+1

In aumento per il tl; dr. Credo che questo fosse il principale punto importante che mancava all'OP. –

Problemi correlati