:: operatore (doppio punto) in Java 8


956

Stavo esplorando il sorgente Java 8 e ho trovato questa parte particolare del codice molto sorprendente:

//defined in IntPipeline.java
@Override
public final OptionalInt reduce(IntBinaryOperator op) {
    return evaluate(ReduceOps.makeInt(op));
}

@Override
public final OptionalInt max() {
    return reduce(Math::max); //this is the gotcha line
}

//defined in Math.java
public static int max(int a, int b) {
    return (a >= b) ? a : b;
}

È Math::maxqualcosa come un puntatore a metodo? Come staticviene convertito un metodo normale IntBinaryOperator?


60
È zucchero sintattico avere il compilatore che genera automaticamente le implementazioni dell'interfaccia in base alla funzione fornita (per rendere tutto più semplice da usare con le basi di codice esistenti).
Neet

4
java.dzone.com/articles/java-lambda-expressions-vs potrebbe aiutare, non ha approfondito l'argomento
Pontus Backlund

8
@Neet non è esattamente "zucchero sintattico", a meno che non si possa dire per cosa. cioè "x è zucchero sintattico per y".
Ingo

6
@Ingo crea un nuovo oggetto di lambda ogni volta che lo uso. TestingLambda$$Lambda$2/8460669e TestingLambda$$Lambda$3/11043253sono stati creati su due invocazioni.
Narendra Pathai,

13
I lambda e i riferimenti ai metodi non sono "semplici classi interne anonime". Vedi programmers.stackexchange.com/a/181743/59134 . Sì, se necessario, nuove classi ed istanze vengono create al volo, se necessario, ma solo se necessario.
Stuart segna il

Risposte:


1023

Di solito, si chiamerebbe il reducemetodo usando Math.max(int, int)come segue:

reduce(new IntBinaryOperator() {
    int applyAsInt(int left, int right) {
        return Math.max(left, right);
    }
});

Ciò richiede molta sintassi solo per chiamare Math.max. È qui che entrano in gioco le espressioni lambda. Da Java 8 è permesso fare la stessa cosa in un modo molto più breve:

reduce((int left, int right) -> Math.max(left, right));

Come funziona? Il compilatore java "rileva", che si desidera implementare un metodo che accetta due se ne intrestituisce uno int. Ciò equivale ai parametri formali dell'unico e unico metodo di interfaccia IntBinaryOperator(il parametro del metodo che reducesi desidera chiamare). Quindi il compilatore fa il resto per te - presume solo che tu voglia implementarloIntBinaryOperator .

Ma poiché esso Math.max(int, int)soddisfa i requisiti formali di IntBinaryOperator, può essere utilizzato direttamente. Poiché Java 7 non ha alcuna sintassi che consenta di passare un metodo stesso come argomento (è possibile passare solo i risultati del metodo, ma mai i riferimenti al metodo), la ::sintassi è stata introdotta in Java 8 per fare riferimento ai metodi:

reduce(Math::max);

Nota che questo sarà interpretato dal compilatore, non dalla JVM in fase di esecuzione! Sebbene produca bytecode diversi per tutti e tre i frammenti di codice, sono semanticamente uguali, quindi gli ultimi due possono essere considerati versioni brevi (e probabilmente più efficienti) delIntBinaryOperator dell'implementazione sopra!

(Vedi anche Traduzione delle espressioni lambda )


489

::si chiama Metodo di riferimento. È fondamentalmente un riferimento a un singolo metodo. Vale a dire si riferisce a un metodo esistente per nome.

Breve spiegazione : di
seguito è riportato un esempio di riferimento a un metodo statico:

class Hey {
     public static double square(double num){
        return Math.pow(num, 2);
    }
}

Function<Double, Double> square = Hey::square;
double ans = square.apply(23d);

squarepuò essere passato come i riferimenti agli oggetti e attivato quando necessario. In effetti, può essere facilmente usato come riferimento a metodi "normali" di oggetti come staticquelli. Per esempio:

class Hey {
    public double square(double num) {
        return Math.pow(num, 2);
    }
}

Hey hey = new Hey();
Function<Double, Double> square = hey::square;
double ans = square.apply(23d);

Functionsopra è un'interfaccia funzionale . Per comprendere appieno ::, è importante comprendere anche le interfacce funzionali. Chiaramente, un'interfaccia funzionale è un'interfaccia con un solo metodo astratto.

Esempi di interfacce funzionali includono Runnable, Callablee ActionListener.

Functionsopra è un'interfaccia funzionale con un solo metodo: apply. Prende un argomento e produce un risultato.


Il motivo per cui ::sono fantastici è che :

I riferimenti ai metodi sono espressioni che hanno lo stesso trattamento delle espressioni lambda (...), ma invece di fornire un corpo di metodo, fanno riferimento a un metodo esistente per nome.

Ad esempio invece di scrivere il corpo lambda

Function<Double, Double> square = (Double x) -> x * x;

Puoi semplicemente farlo

Function<Double, Double> square = Hey::square;

In fase di esecuzione, questi due squaremetodi si comportano esattamente nello stesso modo l'uno dell'altro. Il bytecode può essere o non essere lo stesso (anche se, per il caso precedente, viene generato lo stesso bytecode; compilare quanto sopra e verificare conjavap -c ).

L'unico criterio principale da soddisfare è: il metodo fornito deve avere una firma simile al metodo dell'interfaccia funzionale che usi come riferimento all'oggetto .

Il seguito è illegale:

Supplier<Boolean> p = Hey::square; // illegal

squaresi aspetta un argomento e restituisce a double. Il getmetodo nel fornitore restituisce un valore ma non accetta un argomento. Pertanto, ciò si traduce in un errore.

Un riferimento al metodo si riferisce al metodo di un'interfaccia funzionale. (Come accennato, le interfacce funzionali possono avere solo un metodo ciascuno).

Alcuni altri esempi: il acceptmetodo in Consumer accetta un input ma non restituisce nulla.

Consumer<Integer> b1 = System::exit;   // void exit(int status)
Consumer<String[]> b2 = Arrays::sort;  // void sort(Object[] a)
Consumer<String> b3 = MyProgram::main; // void main(String... args)

class Hey {
    public double getRandom() {
        return Math.random();
    }
}

Callable<Double> call = hey::getRandom;
Supplier<Double> call2 = hey::getRandom;
DoubleSupplier sup = hey::getRandom;
// Supplier is functional interface that takes no argument and gives a result

Sopra, getRandomnon prende argomenti e restituisce a double. Quindi è possibile utilizzare qualsiasi interfaccia funzionale che soddisfi i criteri di: non prendere argomenti e restituiredouble .

Un altro esempio:

Set<String> set = new HashSet<>();
set.addAll(Arrays.asList("leo","bale","hanks"));
Predicate<String> pred = set::contains;
boolean exists = pred.test("leo");

In caso di tipi parametrizzati :

class Param<T> {
    T elem;
    public T get() {
        return elem;
    }

    public void set(T elem) {
        this.elem = elem;
    }

    public static <E> E returnSame(E elem) {
        return elem;
    }
}

Supplier<Param<Integer>> obj = Param<Integer>::new;
Param<Integer> param = obj.get();
Consumer<Integer> c = param::set;
Supplier<Integer> s = param::get;

Function<String, String> func = Param::<String>returnSame;

I riferimenti ai metodi possono avere stili diversi, ma fondamentalmente significano tutti la stessa cosa e possono essere semplicemente visualizzati come lambda:

  1. Un metodo statico ( ClassName::methName)
  2. Un metodo di istanza di un oggetto particolare ( instanceRef::methName)
  3. Un super metodo di un oggetto particolare ( super::methName)
  4. Un metodo di istanza di un oggetto arbitrario di un tipo particolare ( ClassName::methName)
  5. Un riferimento al costruttore di classe ( ClassName::new)
  6. Un riferimento al costruttore di array ( TypeName[]::new)

Per ulteriori riferimenti, vedere http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html .


6
Grazie per la spiegazione. In breve: '::' usa per estrarre un metodo che soddisfa una FunctionalInterface (lambda): ClassX :: staticMethodX, o instanceX :: instanceMethodX "
jessarah,

55

Sì è vero. L' ::operatore viene utilizzato per il riferimento al metodo. Quindi, si possono estrarre metodi statici dalle classi utilizzandolo o metodi dagli oggetti. Lo stesso operatore può essere utilizzato anche per i costruttori. Tutti i casi menzionati qui sono esemplificati nell'esempio di codice seguente.

La documentazione ufficiale di Oracle è disponibile qui .

Puoi avere una migliore visione d'insieme delle modifiche a JDK 8 in questo articolo. Nella sezione Riferimenti metodo / costruttore viene inoltre fornito un esempio di codice:

interface ConstructorReference {
    T constructor();
}

interface  MethodReference {
   void anotherMethod(String input);
}

public class ConstructorClass {
    String value;

   public ConstructorClass() {
       value = "default";
   }

   public static void method(String input) {
      System.out.println(input);
   }

   public void nextMethod(String input) {
       // operations
   }

   public static void main(String... args) {
       // constructor reference
       ConstructorReference reference = ConstructorClass::new;
       ConstructorClass cc = reference.constructor();

       // static method reference
       MethodReference mr = cc::method;

       // object method reference
       MethodReference mr2 = cc::nextMethod;

       System.out.println(cc.value);
   }
}

una buona spiegazione è quella trovata qui: doanduyhai.wordpress.com/2012/07/14/…
Olimpiu POP

1
@RichardTingle method(Math::max);è l'invocazione e la definizione del metodo sarebbe simile public static void method(IntBinaryOperator op){System.out.println(op.applyAsInt(1, 2));}. Ecco come viene usato.
Narendra Pathai,

2
Per chi ha familiarità con C # è simile a DelegateType d = new DelegateType (MethodName);
Adrian Zanescu,

27

Sembra che sia un po 'tardi, ma qui ci sono i miei due centesimi. Un'espressione lambda è usato per creare metodi anonimi. Non fa altro che chiamare un metodo esistente, ma è più chiaro fare riferimento al metodo direttamente con il suo nome. E il metodo di riferimento ci consente di farlo utilizzando l'operatore metodo di riferimento ::.

Considera la seguente semplice classe in cui ogni dipendente ha un nome e un voto.

public class Employee {
    private String name;
    private String grade;

    public Employee(String name, String grade) {
        this.name = name;
        this.grade = grade;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getGrade() {
        return grade;
    }

    public void setGrade(String grade) {
        this.grade = grade;
    }
}

Supponiamo di avere un elenco di dipendenti restituiti con un metodo e che vogliamo ordinare i dipendenti in base al loro grado. Sappiamo di poter utilizzare la classe anonima come:

    List<Employee> employeeList = getDummyEmployees();

    // Using anonymous class
    employeeList.sort(new Comparator<Employee>() {
           @Override
           public int compare(Employee e1, Employee e2) {
               return e1.getGrade().compareTo(e2.getGrade());
           }
    });

dove getDummyEmployee () è un metodo come:

private static List<Employee> getDummyEmployees() {
        return Arrays.asList(new Employee("Carrie", "C"),
                new Employee("Fanishwar", "F"),
                new Employee("Brian", "B"),
                new Employee("Donald", "D"),
                new Employee("Adam", "A"),
                new Employee("Evan", "E")
                );
    }

Ora sappiamo che Comparator è un'interfaccia funzionale. Un'interfaccia funzionale è quella con esattamente un metodo astratto (sebbene possa contenere uno o più metodi predefiniti o statici). L'espressione lambda fornisce l'implementazione in @FunctionalInterfacemodo che un'interfaccia funzionale possa avere solo un metodo astratto. Possiamo usare l'espressione lambda come:

employeeList.sort((e1,e2) -> e1.getGrade().compareTo(e2.getGrade())); // lambda exp

Sembra tutto buono, ma cosa succede se la classe Employeefornisce anche un metodo simile:

public class Employee {
    private String name;
    private String grade;
    // getter and setter
    public static int compareByGrade(Employee e1, Employee e2) {
        return e1.grade.compareTo(e2.grade);
    }
}

In questo caso l'utilizzo del nome del metodo stesso sarà più chiaro. Quindi possiamo fare direttamente riferimento al metodo usando il riferimento al metodo come:

employeeList.sort(Employee::compareByGrade); // method reference

Secondo i documenti ci sono quattro tipi di riferimenti a metodi:

+----+-------------------------------------------------------+--------------------------------------+
|    | Kind                                                  | Example                              |
+----+-------------------------------------------------------+--------------------------------------+
| 1  | Reference to a static method                          | ContainingClass::staticMethodName    |
+----+-------------------------------------------------------+--------------------------------------+
| 2  |Reference to an instance method of a particular object | containingObject::instanceMethodName | 
+----+-------------------------------------------------------+--------------------------------------+
| 3  | Reference to an instance method of an arbitrary object| ContainingType::methodName           |
|    | of a particular type                                  |                                      |  
+----+-------------------------------------------------------+--------------------------------------+
| 4  |Reference to a constructor                             | ClassName::new                       |
+------------------------------------------------------------+--------------------------------------+

25

::è un nuovo operatore incluso in Java 8 che viene utilizzato per fare riferimento a un metodo di una classe esistente. È possibile fare riferimento a metodi statici e metodi non statici di una classe.

Per il riferimento a metodi statici, la sintassi è:

ClassName :: methodName 

Per fare riferimento a metodi non statici, la sintassi è

objRef :: methodName

E

ClassName :: methodName

L'unico prerequisito per fare riferimento a un metodo è che il metodo esiste in un'interfaccia funzionale, che deve essere compatibile con il riferimento al metodo.

I riferimenti ai metodi, quando valutati, creano un'istanza dell'interfaccia funzionale.

Trovato su: http://www.speakingcs.com/2014/08/method-references-in-java-8.html


22

Questo è un riferimento al metodo in Java 8. La documentazione di Oracle è qui .

Come indicato nella documentazione ...

Il metodo di riferimento Person :: compareByAge è un riferimento a un metodo statico.

Di seguito è riportato un esempio di riferimento a un metodo di istanza di un oggetto particolare:

class ComparisonProvider {
    public int compareByName(Person a, Person b) {
        return a.getName().compareTo(b.getName());
    }

    public int compareByAge(Person a, Person b) {
        return a.getBirthday().compareTo(b.getBirthday());
    }
}

ComparisonProvider myComparisonProvider = new ComparisonProvider();
Arrays.sort(rosterAsArray, myComparisonProvider::compareByName); 

Il riferimento al metodo myComparisonProvider :: compareByName invoca il metodo compareByName che fa parte dell'oggetto myComparisonProvider. JRE deduce gli argomenti del tipo di metodo, che in questo caso sono (Person, Person).


2
ma il metodo 'compareByAge' non è statico.
abbas

3
@abbas Nor è compareByName. Quindi, si accede a questi metodi non statici tramite l'operatore di riferimento utilizzando un oggetto. Se fossero statici, potresti usare il nome della classe come ComparisionProvider :: someStaticMethod
Seshadri R

6

:: L'operatore è stato introdotto in Java 8 per i riferimenti ai metodi. Un riferimento al metodo è la sintassi abbreviata per un'espressione lambda che esegue solo UN metodo. Ecco la sintassi generale di un riferimento al metodo:

Object :: methodName

Sappiamo che possiamo usare espressioni lambda invece di usare una classe anonima. Ma a volte, l'espressione lambda è in realtà solo una chiamata a un metodo, ad esempio:

Consumer<String> c = s -> System.out.println(s);

Per rendere più chiaro il codice, puoi trasformare quell'espressione lambda in un riferimento di metodo:

Consumer<String> c = System.out::println;

3

Il :: è noto come riferimenti a metodi. Diciamo che vogliamo chiamare un metodo di calcoloPrice della classe Acquisto. Quindi possiamo scriverlo come:

Purchase::calculatePrice

Può anche essere visto come una forma breve di scrittura dell'espressione lambda Perché i riferimenti ai metodi vengono convertiti in espressioni lambda.


Posso fare riferimenti a metodi nidificati? ad es. groupingBy (Ordine :: cliente :: nome)

non puoi fare un riferimento al metodo nidificato in questo modo
Kirby,

3

Ho trovato questa fonte molto interessante.

In effetti, è la Lambda che si trasforma in un doppio colon . Il doppio colon è più leggibile. Seguiamo questi passaggi:

PASSO 1:

// We create a comparator of two persons
Comparator c = (Person p1, Person p2) -> p1.getAge().compareTo(p2.getAge());

PASSO 2:

// We use the interference
Comparator c = (p1, p2) -> p1.getAge().compareTo(p2.getAge());

Fase 3:

// The magic using method reference
Comparator c = Comparator.comparing(Person::getAge);

3
Sembra che Person::getAge()dovrebbe essere Person::getAge.
Qwertiy,

2

return reduce(Math::max);NON è UGUALE areturn reduce(max());

Ma significa qualcosa del genere:

IntBinaryOperator myLambda = (a, b)->{(a >= b) ? a : b};//56 keystrokes I had to type -_-
return reduce(myLambda);

Puoi semplicemente salvare 47 sequenze di tasti se scrivi in ​​questo modo

return reduce(Math::max);//Only 9 keystrokes ^_^

2

Poiché molte risposte qui spiegano bene il ::comportamento, vorrei inoltre chiarire che l' :: operatore non deve necessariamente avere la stessa firma dell'interfaccia funzionale di riferimento se viene utilizzato per le variabili di istanza . Supponiamo che abbiamo bisogno di un BinaryOperator che abbia un tipo di TestObject . In modo tradizionale è implementato in questo modo:

BinaryOperator<TestObject> binary = new BinaryOperator<TestObject>() {

        @Override
        public TestObject apply(TestObject t, TestObject u) {

            return t;
        }
    };

Come vedi nell'implementazione anonima, sono necessari due argomenti TestObject e restituisce anche un oggetto TestObject. Per soddisfare questa condizione utilizzando l' ::operatore possiamo iniziare con un metodo statico:

public class TestObject {


    public static final TestObject testStatic(TestObject t, TestObject t2){
        return t;
    }
}

e quindi chiama:

BinaryOperator<TestObject> binary = TestObject::testStatic;

Ok, compilato bene. E se avessimo bisogno di un metodo di istanza? Consente di aggiornare TestObject con il metodo di istanza:

public class TestObject {

    public final TestObject testInstance(TestObject t, TestObject t2){
        return t;
    }

    public static final TestObject testStatic(TestObject t, TestObject t2){
        return t;
    }
}

Ora possiamo accedere all'istanza come di seguito:

TestObject testObject = new TestObject();
BinaryOperator<TestObject> binary = testObject::testInstance;

Questo codice viene compilato correttamente, ma sotto uno non:

BinaryOperator<TestObject> binary = TestObject::testInstance;

Dimmi la mia eclissi "Impossibile fare un riferimento statico al metodo non statico testInstance (TestObject, TestObject) dal tipo TestObject ..."

Abbastanza giusto è un metodo di istanza, ma se sovraccarichiamo testInstancecome di seguito:

public class TestObject {

    public final TestObject testInstance(TestObject t){
        return t;
    }

    public final TestObject testInstance(TestObject t, TestObject t2){
        return t;
    }

    public static final TestObject testStatic(TestObject t, TestObject t2){
        return t;
    }
}

E chiama:

BinaryOperator<TestObject> binary = TestObject::testInstance;

Il codice verrà compilato correttamente. Perché chiamerà testInstancecon un singolo parametro invece di doppio. Ok, quindi cosa sono successi i nostri due parametri? Consente di stampare e vedere:

public class TestObject {

    public TestObject() {
        System.out.println(this.hashCode());
    }

    public final TestObject testInstance(TestObject t){
        System.out.println("Test instance called. this.hashCode:" 
    + this.hashCode());
        System.out.println("Given parameter hashCode:" + t.hashCode());
        return t;
    }

    public final TestObject testInstance(TestObject t, TestObject t2){
        return t;
    }

    public static final TestObject testStatic(TestObject t, TestObject t2){
        return t;
    }
}

Che produrrà:

 1418481495  
 303563356  
 Test instance called. this.hashCode:1418481495
 Given parameter hashCode:303563356

Ok, quindi JVM è abbastanza intelligente da chiamare param1.testInstance (param2). Possiamo usare testInstanceda un'altra risorsa ma non TestObject, ovvero:

public class TestUtil {

    public final TestObject testInstance(TestObject t){
        return t;
    }
}

E chiama:

BinaryOperator<TestObject> binary = TestUtil::testInstance;

Semplicemente non verrà compilato e il compilatore dirà: "Il tipo TestUtil non definisce testInstance (TestObject, TestObject)" . Quindi il compilatore cercherà un riferimento statico se non è dello stesso tipo. Ok, che dire del polimorfismo? Se rimuoviamo i modificatori finali e aggiungiamo la nostra classe SubTestObject :

public class SubTestObject extends TestObject {

    public final TestObject testInstance(TestObject t){
        return t;
    }

}

E chiama:

BinaryOperator<TestObject> binary = SubTestObject::testInstance;

Inoltre non verrà compilato, il compilatore cercherà comunque riferimenti statici. Ma sotto il codice verrà compilato bene poiché sta passando is-a test:

public class TestObject {

    public SubTestObject testInstance(Object t){
        return (SubTestObject) t;
    }

}

BinaryOperator<TestObject> binary = TestObject::testInstance;

* Sto solo studiando, quindi ho capito provando a vedere, sentiti libero di correggermi se sbaglio


2

In java-8 Streams Reducer in lavori semplici è una funzione che accetta due valori come input e restituisce il risultato dopo alcuni calcoli. questo risultato viene inserito nella prossima iterazione.

nel caso di Math: funzione max, il metodo continua a restituire un massimo di due valori passati e alla fine hai il numero più grande in mano.


1

In fase di runtime si comportano esattamente allo stesso modo. Il bytecode può / non essere lo stesso (per sopra Incase, genera lo stesso bytecode (compie sopra e controlla javaap -c;))

In fase di esecuzione si comportano esattamente con lo stesso metodo (matematica :: max); genera la stessa matematica (osservare sopra e controllare javap -c;))


1

Nelle versioni Java precedenti, anziché "::" o lambd, è possibile utilizzare:

public interface Action {
    void execute();
}

public class ActionImpl implements Action {

    @Override
    public void execute() {
        System.out.println("execute with ActionImpl");
    }

}

public static void main(String[] args) {
    Action action = new Action() {
        @Override
        public void execute() {
            System.out.println("execute with anonymous class");
        }
    };
    action.execute();

    //or

    Action actionImpl = new ActionImpl();
    actionImpl.execute();
}

O passando al metodo:

public static void doSomething(Action action) {
    action.execute();
}

1

Quindi vedo qui tonnellate di risposte che sono francamente eccessivamente complicate, e questo è un eufemismo.

La risposta è piuttosto semplice: :: si chiama un metodo Riferimenti https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html

Quindi non copia-incolla, sul link, puoi trovare tutte le informazioni se scorri verso il basso fino alla tabella.


Ora diamo un'occhiata a ciò che è un metodo Riferimenti:

A :: B sostituisce in qualche modo la seguente espressione lambda in linea : (params ...) -> AB (params ...)

Per correlarlo con le tue domande, è necessario capire un'espressione java lambda. Non è difficile.

Un'espressione lambda inline è simile a un'interfaccia funzionale definita (che è un'interfaccia che non ha più e non meno di 1 metodo) . Diamo una breve occhiata a cosa intendo:

InterfaceX f = (x) -> x*x; 

InterfaceX deve essere un'interfaccia funzionale. Qualsiasi interfaccia funzionale, l'unica cosa importante di InterfaceX per quel compilatore è che definisci il formato:

InterfaceX può essere uno di questi:

interface InterfaceX
{
    public Integer callMe(Integer x);
}

o questo

interface InterfaceX
{
    public Double callMe(Integer x);
}

o più generico:

interface InterfaceX<T,U>
{
    public T callMe(U x);
}

Prendiamo il primo caso presentato e l'espressione lambda in linea che abbiamo definito in precedenza.

Prima di Java 8, avresti potuto definirlo in modo simile in questo modo:

 InterfaceX o = new InterfaceX(){
                     public int callMe (int x, int y) 
                       {
                        return x*x;
                       } };

Funzionalmente, è la stessa cosa. La differenza sta più nel modo in cui il compilatore lo percepisce.

Ora che abbiamo dato un'occhiata all'espressione lambda in linea, torniamo a Riferimenti metodo (: :). Diciamo che hai una lezione come questa:

class Q {
        public static int anyFunction(int x)
             {
                 return x+5;
             } 
        }

Poiché method anyFunctions ha gli stessi tipi di InterfaceX callMe , possiamo equivalere a questi due con un metodo di riferimento.

Possiamo scriverlo in questo modo:

InterfaceX o =  Q::anyFunction; 

e questo equivale a questo:

InterfaceX o = (x) -> Q.anyFunction(x);

Un aspetto interessante e un vantaggio dei riferimenti ai metodi sono che all'inizio, fino a quando non li si assegna alle variabili, non sono tipabili. Quindi puoi passarli come parametri a qualsiasi interfaccia funzionale dall'aspetto equivalente (ha gli stessi tipi definiti). Questo è esattamente ciò che accade nel tuo caso


1

Le risposte precedenti sono abbastanza complete riguardo a cosa :: che fa riferimento al metodo. Per riassumere, fornisce un modo per fare riferimento a un metodo (o costruttore) senza eseguirlo e, quando valutato, crea un'istanza dell'interfaccia funzionale che fornisce il contesto del tipo di destinazione.

Di seguito sono riportati due esempi per trovare un oggetto con il valore massimo in un ArrayListWITH e WITHOUT l'uso del ::metodo di riferimento. Le spiegazioni sono nei commenti qui sotto.


SENZA l'uso di ::

import java.util.*;

class MyClass {
    private int val;
    MyClass (int v) { val = v; }
    int getVal() { return val; }
}

class ByVal implements Comparator<MyClass> {
    // no need to create this class when using method reference
    public int compare(MyClass source, MyClass ref) {
        return source.getVal() - ref.getVal();
    }
}

public class FindMaxInCol {
    public static void main(String args[]) {
        ArrayList<MyClass> myClassList = new ArrayList<MyClass>();
        myClassList.add(new MyClass(1));
        myClassList.add(new MyClass(0));
        myClassList.add(new MyClass(3));
        myClassList.add(new MyClass(6));

        MyClass maxValObj = Collections.max(myClassList, new ByVal());
    }
}

CON l'uso di ::

import java.util.*;

class MyClass {
    private int val;
    MyClass (int v) { val = v; }
    int getVal() { return val; }
}

public class FindMaxInCol {
    static int compareMyClass(MyClass source, MyClass ref) {
        // This static method is compatible with the compare() method defined by Comparator. 
        // So there's no need to explicitly implement and create an instance of Comparator like the first example.
        return source.getVal() - ref.getVal();
    }

    public static void main(String args[]) {
        ArrayList<MyClass> myClassList = new ArrayList<MyClass>();
        myClassList.add(new MyClass(1));
        myClassList.add(new MyClass(0));
        myClassList.add(new MyClass(3));
        myClassList.add(new MyClass(6));

        MyClass maxValObj = Collections.max(myClassList, FindMaxInCol::compareMyClass);
    }
}

-1

L' ::operatore double colon ie viene introdotto in Java 8 come riferimento di metodo . Il riferimento al metodo è una forma di espressione lambda che viene utilizzata per fare riferimento al metodo esistente con il suo nome.

classname :: methodName

ex:-

  • stream.forEach(element -> System.out.println(element))

Utilizzando Double Colon ::

  • stream.forEach(System.out::println(element))
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.