colon - method as parameter java 8




Operador::(dois-pontos duplos) no Java 8 (11)

As respostas anteriores são bem completas em relação à referência :: method. Resumindo, ele fornece uma maneira de se referir a um método (ou construtor) sem executá-lo e, quando avaliado, cria uma instância da interface funcional que fornece o contexto do tipo de destino.

Abaixo estão dois exemplos para encontrar um objeto com o valor máximo em uma referência de método ArrayList WITH e WITHOUT of :: . Explicações estão nos comentários abaixo.

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

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

Eu estava explorando a fonte do Java 8 e achei essa parte específica do código muito surpreendente:

//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::max algo como um ponteiro de método? Como um método static normal é convertido em IntBinaryOperator ?


Como muitas respostas aqui explicaram bem :: comportamento, adicionalmente gostaria de esclarecer que :: operador não precisa ter exatamente a mesma assinatura da Interface Funcional de referência, se for usada para variáveis ​​de instância . Vamos supor que precisamos de um BinaryOperator que tenha o tipo de TestObject . De maneira tradicional é implementado assim:

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

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

            return t;
        }
    };

Como você vê na implementação anônima, ele requer dois argumentos TestObject e também retorna um objeto TestObject. Para satisfazer essa condição usando :: operator, podemos começar com um método estático:

public class TestObject {


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

e depois ligue:

BinaryOperator<TestObject> binary = TestObject::testStatic;

Ok, compilou bem. E se precisarmos de um método de instância? Vamos atualizar o TestObject com o método da instância:

public class TestObject {

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

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

Agora podemos acessar a instância como abaixo:

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

Este código compila bem, mas abaixo de um não:

BinaryOperator<TestObject> binary = TestObject::testInstance;

Meu eclipse me diz "Não é possível fazer uma referência estática ao método não estático testInstance (TestObject, TestObject) do tipo TestObject ..."

É justo que seja um método de instância, mas se sobrecarregarmos testInstance como abaixo:

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 ligue:

BinaryOperator<TestObject> binary = TestObject::testInstance;

O código só vai compilar bem. Porque ele chamará testInstance com um único parâmetro em vez de um duplo. Ok então o que aconteceu com nossos dois parâmetros? Vamos imprimir e 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;
    }
}

Qual será a saída:

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

Ok, então a JVM é inteligente o suficiente para chamar param1.testInstance (param2). Podemos usar testInstance de outro recurso, mas não TestObject, ou seja:

public class TestUtil {

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

E ligue:

BinaryOperator<TestObject> binary = TestUtil::testInstance;

Apenas não compilará e o compilador dirá: "O tipo TestUtil não define testInstance (TestObject, TestObject)" . Então, o compilador irá procurar por uma referência estática se ela não for do mesmo tipo. Ok, e o polimorfismo? Se removermos os modificadores finais e adicionarmos nossa classe SubTestObject :

public class SubTestObject extends TestObject {

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

}

E ligue:

BinaryOperator<TestObject> binary = SubTestObject::testInstance;

Ele não compilará também, o compilador ainda procurará por referências estáticas. Mas abaixo código irá compilar bem, uma vez que está passando é um teste:

public class TestObject {

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

}

BinaryOperator<TestObject> binary = TestObject::testInstance;

* Eu estou apenas estudando, então eu descobri, tente e veja, sinta-se livre para me corrigir se eu estiver errado


Então eu vejo aqui toneladas de respostas que são francamente supercomplicadas, e isso é um eufemismo.

A resposta é bem simples: :: ela é chamada de Referências do Método https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html

Então, eu não vou copiar-colar, no link, você pode encontrar todas as informações, se você rolar para baixo para a tabela.

Agora, vamos dar uma breve olhada no que é um método de referências:

A :: B um pouco substitui a seguinte expressão lambda inline : (params ...) -> AB (params ...)

Para correlacionar isso com suas perguntas, é necessário entender uma expressão java lambda. Que não é difícil.

Uma expressão lambda inline é semelhante a uma interface funcional definida (que é uma interface que não tem mais nem menos de um método) . Vamos dar uma olhada no que eu quero dizer:

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

InterfaceX deve ser uma interface funcional. Qualquer interface funcional, a única coisa que é importante sobre o InterfaceX para esse compilador é que você defina o formato:

InterfaceX pode ser qualquer um destes:

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

ou isto

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

ou mais genérico:

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

Vamos pegar o primeiro caso apresentado e a expressão lambda inline que definimos anteriormente.

Antes do Java 8, você poderia defini-lo da seguinte maneira:

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

Funcionalmente, é a mesma coisa. A diferença é mais em como o compilador percebe isso.

Agora que analisamos a expressão lambda inline, vamos retornar às referências de método (: :). Digamos que você tenha uma aula assim:

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

Como o método anyFunctions possui os mesmos tipos que o InterfaceX, podemos compará- los com uma Referência de método.

Podemos escrever assim:

InterfaceX o =  Q::anyFunction; 

e isso é equivalente a isto:

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

Uma coisa legal e vantagem das referências de método é que, em primeiro lugar, até que você as atribua a variáveis, elas são sem tipo. Então você pode passá-los como parâmetros para qualquer interface funcional de aparência equivalente (tem os mesmos tipos definidos). O que é exatamente o que acontece no seu caso


Esta é uma referência de método no Java 8. A documentação do Oracle está here .

Como afirmado na documentação ...

A referência do método Person :: compareByAge é uma referência a um método estático.

A seguir, um exemplo de uma referência a um método de instância de um objeto específico:

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

A referência do método myComparisonProvider :: compareByName invoca o método compareByName que faz parte do objeto myComparisonProvider. O JRE infere os argumentos de tipo de método, que neste caso são (Person, Person).


Nas versões mais antigas do Java, em vez de "::" ou lambd, você pode 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();
}

Ou passando para o método:

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

No java-8 Streams Reducer em trabalhos simples é uma função que toma dois valores como entrada e retorna o resultado após algum cálculo. esse resultado é alimentado na próxima iteração.

no caso da função Math: max, o método continua retornando no máximo dois valores passados ​​e no final você tem o maior número em mãos.


O :: é conhecido como referências de método. Vamos dizer que queremos chamar um método calculatePrice da classe Purchase. Então podemos escrever como:

Purchase::calculatePrice

Também pode ser visto como forma abreviada de escrever a expressão lambda Como as referências de método são convertidas em expressões lambda.


Parece um pouco tarde, mas aqui estão meus dois centavos. Uma expressão lambda é usada para criar métodos anônimos. Não faz nada além de chamar um método existente, mas é mais claro se referir ao método diretamente pelo seu nome. E a referência de método nos permite fazer isso usando o operador de referência de método :: .

Considere a seguinte classe simples em que cada funcionário tem um nome e uma nota.

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

Suponha que tenhamos uma lista de funcionários retornados por algum método e queremos ordenar os funcionários por sua nota. Sabemos que podemos usar classes anônimas 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());
           }
    });

onde getDummyEmployee () é algum método como:

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

Agora sabemos que o Comparator é uma interface funcional. Uma Interface Funcional é aquela com exatamente um método abstrato (embora possa conter um ou mais métodos padrão ou estáticos). Então podemos usar a expressão lambda como:

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

Parece tudo bem, mas e se a classe Employee também fornece um 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);
    }
}

Neste caso, usar o nome do método em si será mais claro. Portanto, podemos nos referir diretamente ao método usando a referência de método como:

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

De acordo com os documentos, existem quatro tipos de referências 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                       |
+------------------------------------------------------------+--------------------------------------+

:: é chamado Referência de Método. É basicamente uma referência a um único método. Ou seja, refere-se a um método existente pelo nome.

Breve explicação :
Abaixo está um exemplo de uma referência a um 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);

square pode ser passado como referências de objetos e acionado quando necessário. Na verdade, ele pode ser usado tão facilmente quanto uma referência a métodos "normais" de objetos como os static . Por exemplo:

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

Function acima é uma interface funcional . Para entender completamente :: , é importante entender as interfaces funcionais também. Obviamente, uma interface funcional é uma interface com apenas um método abstrato.

Exemplos de interfaces funcionais incluem Runnable , Callable e ActionListener .

Function acima é uma interface funcional com apenas um método: apply . Leva um argumento e produz um resultado.

A razão pela qual :: s é incrível é that :

As referências de método são expressões que têm o mesmo tratamento que as expressões lambda (...), mas em vez de fornecer um corpo de método, elas referem um método existente por nome.

Por exemplo, em vez de escrever o corpo lambda

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

Você pode simplesmente fazer

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

Em tempo de execução, esses dois métodos square se comportam exatamente da mesma forma que os outros. O bytecode pode ou não ser o mesmo (embora, para o caso acima, o mesmo bytecode seja gerado; compile o acima e verifique com javap -c ).

O único critério principal a ser satisfeito é: o método que você fornece deve ter uma assinatura semelhante ao método da interface funcional que você usa como referência de objeto .

O abaixo é ilegal:

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

square espera um argumento e retorna um double . O método get no Supplier espera um argumento, mas não retorna nada. Assim, isso resulta em um erro.

Uma referência de método refere-se ao método de uma interface funcional. (Como mencionado, as interfaces funcionais podem ter apenas um método cada).

Mais alguns exemplos: o método accept no Consumer recebe uma entrada, mas não retorna 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

Acima, getRandom não recebe argumentos e retorna um double . Portanto, qualquer interface funcional que satisfaça os critérios de: sem argumento e retorno double pode ser usada.

Outro exemplo:

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

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

Referências de método podem ter estilos diferentes, mas fundamentalmente todas elas significam a mesma coisa e podem simplesmente ser visualizadas como lambdas:

  1. Um método estático ( ClassName::methName )
  2. Um método de instância de um objeto específico ( instanceRef::methName )
  3. Um super método de um objeto em particular ( super::methName )
  4. Um método de instância de um objeto arbitrário de um tipo específico ( ClassName::methName )
  5. Uma referência de construtor de classe ( ClassName::new )
  6. Uma referência de construtor de matriz ( TypeName[]::new )

Para mais referências, consulte that .


:: é um novo operador incluído no Java 8 que é usado para referenciar um método de uma classe existente. Você pode consultar métodos estáticos e métodos não estáticos de uma classe.

Para referenciar métodos estáticos, a sintaxe é:

ClassName :: methodName 

Para referir métodos não estáticos, a sintaxe é

objRef :: methodName

E

ClassName :: methodName

O único pré-requisito para referenciar um método é que o método existe em uma interface funcional, que deve ser compatível com a referência do método.

Referências de métodos, quando avaliadas, criam uma instância da interface funcional.

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


:: O operador foi introduzido no java 8 para referências de métodos. Uma referência de método é a sintaxe abreviada de uma expressão lambda que executa apenas UM método. Aqui está a sintaxe geral de uma referência de método:

Object :: methodName

Sabemos que podemos usar expressões lambda em vez de usar uma classe anônima. Mas às vezes, a expressão lambda é realmente apenas uma chamada para algum método, por exemplo:

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

Para tornar o código mais claro, você pode transformar essa expressão lambda em uma referência de método:

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




java-8