:: Operador (doble colon) en Java 8

956

Estaba explorando la fuente de Java 8 y encontré esta parte particular del código muy 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;
}

¿Es Math::maxalgo así como un puntero de método? ¿Cómo funciona un normalstatic se convierte métodoIntBinaryOperator ?

Narendra Pathai
fuente
6060
Es muy útil tener implementaciones de interfaz de generación automática del compilador basadas en la función que proporciona (para hacer que todo el lambda sea más fácil de usar con las bases de código existentes).
Neet
44
java.dzone.com/articles/java-lambda-expressions-vs podría ayudar, no se profundizó en el tema
Pontus Backlund
8
@Neet no es exactamente "azúcar sintáctico", a menos que puedas decir para qué. es decir, "x es azúcar sintáctico para y".
Ingo
66
@Ingo crea un nuevo objeto de lambda cada vez que lo uso. TestingLambda$$Lambda$2/8460669y TestingLambda$$Lambda$3/11043253fueron creados en dos invocaciones.
Narendra Pathai
13
Las referencias lambdas y de métodos no son "clases internas anónimas simples". Ver programmers.stackexchange.com/a/181743/59134 . Sí, si es necesario, se crean nuevas clases e instancias sobre la marcha, si es necesario, pero solo si es necesario.
Stuart Marks

Respuestas:

1024

Por lo general, uno llamaría al reducemétodo de la Math.max(int, int)siguiente manera:

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

Eso requiere mucha sintaxis para solo llamar Math.max. Ahí es donde entran en juego las expresiones lambda. Desde Java 8 está permitido hacer lo mismo de una manera mucho más corta:

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

¿Como funciona esto? El compilador de Java "detecta" que desea implementar un método que acepte dos intsy devuelva uno int. Esto es equivalente a los parámetros formales del único método de interfaz IntBinaryOperator(el parámetro del métodoreduce que desea llamar). Entonces el compilador hace el resto por usted, solo asume que desea implementar IntBinaryOperator.

Pero como Math.max(int, int)cumple con los requisitos formales de IntBinaryOperator, se puede usar directamente. Debido a que Java 7 no tiene ninguna sintaxis que permita pasar un método en sí mismo como argumento (solo puede pasar resultados de métodos, pero nunca referencias de métodos), la ::sintaxis se introdujo en Java 8 para hacer referencia a métodos:

reduce(Math::max);

Tenga en cuenta que esto será interpretado por el compilador, no por la JVM en tiempo de ejecución. Aunque produce diferentes códigos de bytes para los tres fragmentos de código, son semánticamente iguales, por lo que los dos últimos pueden considerarse versiones cortas (y probablemente más eficientes) deIntBinaryOperator implementación anterior.

(Ver también Traducción de expresiones lambda )

isnot2bad
fuente
489

::se llama Método de referencia. Básicamente es una referencia a un único método. Es decir, se refiere a un método existente por su nombre.

Explicación breve : a
continuación se muestra un ejemplo de una referencia a un método estático:

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

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

squarepuede pasarse como referencias a objetos y activarse cuando sea necesario. De hecho, puede usarse tan fácilmente como una referencia a métodos "normales" de objetos como staticlos. Por ejemplo:

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);

Functionarriba es una interfaz funcional . Para comprender completamente ::, también es importante comprender las interfaces funcionales. Claramente, una interfaz funcional es una interfaz con un solo método abstracto.

Ejemplos de interfaces funcionales incluyen Runnable, Callable, y ActionListener.

Functionanterior es una interfaz funcional con un solo método: apply. Toma un argumento y produce un resultado.


La razón por la que los ::s son increíbles es que :

Las referencias a métodos son expresiones que tienen el mismo tratamiento que las expresiones lambda (...), pero en lugar de proporcionar un cuerpo de método, hacen referencia a un método existente por su nombre.

Por ejemplo, en lugar de escribir el cuerpo lambda

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

Simplemente puedes hacer

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

En tiempo de ejecución, estos dos squaremétodos se comportan exactamente igual que el otro. El bytecode puede o no ser el mismo (aunque, para el caso anterior, se genera el mismo bytecode; compile lo anterior y verifique con javap -c).

El único criterio importante para satisfacer es: el método que proporcione debe tener una firma similar al método de la interfaz funcional que utiliza como referencia de objeto .

Lo siguiente es ilegal:

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

squareespera un argumento y devuelve a double. El getmétodo en Proveedor devuelve un valor pero no toma un argumento. Por lo tanto, esto da como resultado un error.

Una referencia de método se refiere al método de una interfaz funcional. (Como se mencionó, las interfaces funcionales solo pueden tener un método cada una).

Algunos ejemplos más: el acceptmétodo en Consumer toma una entrada pero no devuelve nada.

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

Arriba, getRandomno toma argumentos y devuelve a double. Por lo tanto, se puede utilizar cualquier interfaz funcional que satisfaga los criterios de: no tomar argumento y volverdouble .

Otro ejemplo:

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

En caso de tipos parametrizados :

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;

Las referencias de métodos pueden tener diferentes estilos, pero fundamentalmente todas significan lo mismo y simplemente se pueden visualizar como lambdas:

  1. Un método estático ( ClassName::methName)
  2. Un método de instancia de un objeto particular ( instanceRef::methName)
  3. Un súper método de un objeto particular ( super::methName)
  4. Un método de instancia de un objeto arbitrario de un tipo particular ( ClassName::methName)
  5. Una referencia de constructor de clase ( ClassName::new)
  6. Una referencia de constructor de matriz ( TypeName[]::new)

Para obtener más información, consulte http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html .

Jatin
fuente
66
Gracias por la explicación. En resumen: '::' se usa para extraer un método que satisfaga una FunctionalInterface (lambda): ClassX :: staticMethodX, o instanceX :: instanceMethodX "
jessarah
55

Si eso es verdad. El ::operador se utiliza para referenciar métodos. Entonces, uno puede extraer métodos estáticos de las clases usándolo o métodos de objetos. El mismo operador se puede utilizar incluso para constructores. Todos los casos mencionados aquí se ejemplifican en el ejemplo de código a continuación.

La documentación oficial de Oracle se puede encontrar aquí .

Puede tener una mejor visión general de los cambios de JDK 8 en este artículo. En la sección de referencia Método / Constructor también se proporciona un ejemplo de código:

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);
   }
}
Olimpiu POP
fuente
una buena explicación es la que se encuentra aquí: doanduyhai.wordpress.com/2012/07/14/…
Olimpiu POP
1
@ RichardTingle method(Math::max);es invocación y la definición del método sería similar public static void method(IntBinaryOperator op){System.out.println(op.applyAsInt(1, 2));}. Así es como se usa.
Narendra Pathai
2
Para aquellos familiarizados con C #, es similar con DelegateType d = new DelegateType (MethodName);
Adrian Zanescu
27

Parece que es un poco tarde, pero aquí están mis dos centavos. Una expresión lambda se usa para crear métodos anónimos. No hace nada más que llamar a un método existente, pero es más claro referirse al método directamente por su nombre. Y la referencia de método nos permite hacerlo utilizando el operador de referencia de método ::.

Considere la siguiente clase simple donde cada empleado tiene un nombre y grado.

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;
    }
}

Supongamos que tenemos una lista de empleados devueltos por algún método y queremos clasificar a los empleados por su grado. Sabemos que podemos hacer uso de la clase anónima como:

    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());
           }
    });

donde getDummyEmployee () es un método como:

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")
                );
    }

Ahora sabemos que Comparator es una interfaz funcional. Una interfaz funcional es la que tiene exactamente un método abstracto (aunque puede contener uno o más métodos predeterminados o estáticos). La expresión Lambda proporciona la implementación de @FunctionalInterfacemodo que una interfaz funcional solo puede tener un método abstracto. Podemos usar la expresión lambda como:

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

Parece todo bien, pero ¿y si la clase Employeetambién proporciona un método similar?

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);
    }
}

En este caso, usar el nombre del método en sí será más claro. Por lo tanto, podemos referirnos directamente al método utilizando la referencia del método como:

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

Según los documentos, hay cuatro tipos de referencias de métodos:

+----+-------------------------------------------------------+--------------------------------------+
|    | 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                       |
+------------------------------------------------------------+--------------------------------------+
akhil_mittal
fuente
25

::es un nuevo operador incluido en Java 8 que se usa para referir un método de una clase existente. Puede consultar métodos estáticos y métodos no estáticos de una clase.

Para referirse a métodos estáticos, la sintaxis es:

ClassName :: methodName 

Para hacer referencia a métodos no estáticos, la sintaxis es

objRef :: methodName

Y

ClassName :: methodName

El único requisito previo para referir un método es que ese método exista en una interfaz funcional, que debe ser compatible con la referencia del método.

Las referencias de métodos, cuando se evalúan, crean una instancia de la interfaz funcional.

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

sreenath
fuente
22

Esta es una referencia de método en Java 8. La documentación de Oracle está aquí .

Como se indica en la documentación ...

La referencia de método Person :: compareByAge es una referencia a un método estático.

El siguiente es un ejemplo de una referencia a un método de instancia de un objeto particular:

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); 

La referencia de método myComparisonProvider :: compareByName invoca el método compareByName que forma parte del objeto myComparisonProvider. El JRE infiere los argumentos de tipo de método, que en este caso son (Persona, Persona).

david99world
fuente
2
pero el método 'compareByAge' no es estático.
abbas
3
@abbas tampoco es compareByName. Por lo tanto, accede a estos métodos no estáticos a través del operador de referencia utilizando un objeto. Si fueran estáticos, podría usar el nombre de la clase como ComparisionProvider :: someStaticMethod
Seshadri R
6

:: El operador se introdujo en Java 8 para referencias de métodos. Una referencia de método es la sintaxis abreviada para una expresión lambda que ejecuta solo UN método. Aquí está la sintaxis general de una referencia de método:

Object :: methodName

Sabemos que podemos usar expresiones lambda en lugar de usar una clase anónima. Pero a veces, la expresión lambda es realmente solo una llamada a algún método, por ejemplo:

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

Para aclarar el código, puede convertir esa expresión lambda en una referencia de método:

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

El :: se conoce como referencias de método. Digamos que queremos llamar a un método CalculatePrice de la clase Compra. Entonces podemos escribirlo como:

Purchase::calculatePrice

También se puede ver como una forma corta de escribir la expresión lambda porque las referencias de métodos se convierten en expresiones lambda.

Sonu
fuente
¿Puedo hacer referencias de métodos anidados? por ejemplo groupingBy (Order :: customer :: name)
no puede hacer una referencia de método anidado de esa manera
Kirby
3

Encontré esta fuente muy interesante.

De hecho, es la Lambda la que se convierte en un doble colon . El doble colon es más legible. Seguimos esos pasos:

PASO 1:

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

PASO 2:

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

PASO 3:

// The magic using method reference
Comparator c = Comparator.comparing(Person::getAge);
Houssem Badri
fuente
3
Parece que Person::getAge()debería ser Person::getAge.
Qwertiy
2

return reduce(Math::max);es DIFERENTE areturn reduce(max());

Pero significa, algo como esto:

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

Puede guardar 47 pulsaciones de teclas si escribe así

return reduce(Math::max);//Only 9 keystrokes ^_^
Jude Niroshan
fuente
2

Dado que muchas respuestas aquí explicaron bien el ::comportamiento, adicionalmente me gustaría aclarar que el :: operador no necesita tener exactamente la misma firma que la interfaz funcional de referencia si se usa para variables de instancia . Supongamos que necesitamos un BinaryOperator que tenga el tipo de TestObject . De manera tradicional se implementa así:

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

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

            return t;
        }
    };

Como puede ver en la implementación anónima, requiere dos argumentos TestObject y también devuelve un objeto TestObject. Para satisfacer esta condición utilizando el ::operador, podemos comenzar con un método estático:

public class TestObject {


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

y luego llame a:

BinaryOperator<TestObject> binary = TestObject::testStatic;

Ok, se compiló bien. ¿Qué pasa si necesitamos un método de instancia? Permite actualizar TestObject con el método de instancia:

public class TestObject {

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

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

Ahora podemos acceder a la instancia de la siguiente manera:

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

Este código se compila bien, pero debajo de uno no:

BinaryOperator<TestObject> binary = TestObject::testInstance;

Mi eclipse me dice "No se puede hacer una referencia estática al método no estático testInstance (TestObject, TestObject) del tipo TestObject ..."

Es justo que sea un método de instancia, pero si lo sobrecargamos de la testInstancesiguiente manera:

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;
    }
}

Y llama:

BinaryOperator<TestObject> binary = TestObject::testInstance;

El código simplemente se compilará bien. Porque llamará testInstancecon un solo parámetro en lugar de uno doble. Ok, entonces, ¿qué pasó con nuestros dos parámetros? Permite imprimir y ver:

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;
    }
}

Lo que dará salida:

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

Ok, entonces JVM es lo suficientemente inteligente como para llamar a param1.testInstance (param2). ¿Podemos usar testInstancedesde otro recurso pero no TestObject, es decir:

public class TestUtil {

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

Y llama:

BinaryOperator<TestObject> binary = TestUtil::testInstance;

Simplemente no se compilará y el compilador dirá: "El tipo TestUtil no define testInstance (TestObject, TestObject)" . Entonces el compilador buscará una referencia estática si no es del mismo tipo. Ok, ¿y el polimorfismo? Si eliminamos los modificadores finales y agregamos nuestro SubTestObject clase :

public class SubTestObject extends TestObject {

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

}

Y llama:

BinaryOperator<TestObject> binary = SubTestObject::testInstance;

No se compilará también, el compilador seguirá buscando referencias estáticas. Pero el siguiente código se compilará bien ya que está pasando una prueba:

public class TestObject {

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

}

BinaryOperator<TestObject> binary = TestObject::testInstance;

* Solo estoy estudiando, así que me di cuenta al intentar ver, siéntase libre de corregirme si estoy equivocado

HRgiger
fuente
2

En java-8 Streams Reducer en trabajos simples es una función que toma dos valores como entrada y devuelve el resultado después de algunos cálculos. este resultado se alimenta en la próxima iteración.

en el caso de Math: función max, el método sigue devolviendo el máximo de dos valores pasados ​​y al final tiene el mayor número disponible.

Pramod
fuente
1

En tiempo de ejecución se comportan exactamente igual. El código de bytes puede / no ser el mismo (para el caso anterior, genera el mismo código de bytes (compile arriba y verifique javaap -c;))

En tiempo de ejecución, se comportan exactamente igual. Método (math :: max) ;, genera la misma matemática (compilar arriba y verificar javap -c;))

Alfa khatoon
fuente
1

En versiones anteriores de Java, en lugar de "::" o lambd, puede usar:

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 pasando al método:

public static void doSomething(Action action) {
    action.execute();
}
Kamil Tomasz Jarmusik
fuente
1

Así que veo aquí toneladas de respuestas que son francamente complicadas, y eso es un eufemismo.

La respuesta es bastante simple: :: se llama un Método Referencias https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html

Por lo tanto, no copiaré y pegaré, en el enlace, puede encontrar toda la información si se desplaza hacia abajo a la tabla.


Ahora, echemos un vistazo a lo que es un método de referencias:

A :: B sustituye de alguna manera la siguiente expresión lambda en línea : (parámetros ...) -> AB (parámetros ...)

Para correlacionar esto con sus preguntas, es necesario comprender una expresión java lambda. Lo cual no es difícil.

Una expresión lambda en línea es similar a una interfaz funcional definida (que es una interfaz que no tiene más y no menos de 1 método) . Echemos un vistazo a lo que quiero decir:

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

InterfaceX debe ser una interfaz funcional. Cualquier interfaz funcional, lo único importante de InterfaceX para ese compilador es que defina el formato:

InterfaceX puede ser cualquiera de esto:

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

o esto

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

o más genérico:

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

Tomemos el primer caso presentado y la expresión lambda en línea que definimos anteriormente.

Antes de Java 8, podría haberlo definido de manera similar de esta manera:

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

Funcionalmente, es lo mismo. La diferencia es más en cómo el compilador percibe esto.

Ahora que echamos un vistazo a la expresión lambda en línea, volvamos a Referencias de métodos (: :). Digamos que tienes una clase como esta:

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

Dado que el método anyFunctions tiene los mismos tipos que InterfaceX callMe , podemos equivaler esos dos con una Referencia de método.

Podemos escribirlo así:

InterfaceX o =  Q::anyFunction; 

y eso es equivalente a esto:

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

Una cosa interesante y una ventaja de las Referencias de métodos es que al principio, hasta que las asignes a variables, no tienen tipo. Por lo tanto, puede pasarlos como parámetros a cualquier interfaz funcional de aspecto equivalente (tiene los mismos tipos definidos). Que es exactamente lo que pasa en tu caso

Nertan Lucian
fuente
1

Las respuestas anteriores son bastante completas sobre qué :: método de referencia hace. En resumen, proporciona una forma de referirse a un método (o constructor) sin ejecutarlo, y cuando se evalúa, crea una instancia de la interfaz funcional que proporciona el contexto de tipo de destino.

A continuación se muestran dos ejemplos para encontrar un objeto con el valor máximo en un ArrayListWITH y SIN el uso del ::método de referencia. Las explicaciones están en los comentarios a continuación.


SIN el uso de ::

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 el uso de ::

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);
    }
}
Liutong Chen
fuente
-1

El ::operador de dos puntos dobles, es decir , se introduce en Java 8 como referencia de método . La referencia de método es una forma de expresión lambda que se utiliza para referirse al método existente por su nombre.

classname :: methodName

ex:-

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

Mediante el uso de doble colon ::

  • stream.forEach(System.out::println(element))
ishant kulshreshtha
fuente