Eu estava explorando a fonte Java 8 e achei esta 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 static
método normal é convertido IntBinaryOperator
?
TestingLambda$$Lambda$2/8460669
eTestingLambda$$Lambda$3/11043253
foram criados em duas invocações.Respostas:
Geralmente, alguém poderia chamar o
reduce
método usandoMath.max(int, int)
o seguinte:Isso requer muita sintaxe para apenas chamar
Math.max
. É aí que as expressões lambda entram em cena. Desde o Java 8, é permitido fazer a mesma coisa de uma maneira muito mais curta:Como é que isso funciona? O compilador java "detecta" que você deseja implementar um método que aceita dois se
int
retorna umint
. Isso é equivalente aos parâmetros formais do primeiro e único método de interfaceIntBinaryOperator
(o parâmetro do método quereduce
você deseja chamar). Portanto, o compilador faz o resto por você - apenas assume que você deseja implementarIntBinaryOperator
.Mas como
Math.max(int, int)
ele cumpre os requisitos formais deIntBinaryOperator
, pode ser usado diretamente. Como o Java 7 não possui nenhuma sintaxe que permita que um método seja transmitido como argumento (você só pode passar resultados de métodos, mas nunca referências a métodos), a::
sintaxe foi introduzida no Java 8 para referenciar métodos:Observe que isso será interpretado pelo compilador, não pela JVM em tempo de execução! Embora produza bytecodes diferentes para todos os três trechos de código, eles são semanticamente iguais, portanto os dois últimos podem ser considerados versões curtas (e provavelmente mais eficientes) da
IntBinaryOperator
implementação acima!(Veja também a tradução de expressões lambda )
fonte
::
é chamado de Referência de método. É basicamente uma referência a um único método. Ou seja, refere-se a um método existente por nome.Breve explicação :
Abaixo está um exemplo de uma referência a um método estático:
square
pode ser transmitido como referências a objetos e acionado quando necessário. De fato, ele pode ser usado tão facilmente quanto uma referência a métodos "normais" de objetosstatic
. Por exemplo:Function
acima é uma interface funcional . Para entender completamente::
, é importante entender também as interfaces funcionais. Claramente, uma interface funcional é uma interface com apenas um método abstrato.Exemplos das interfaces funcionais incluem
Runnable
,Callable
eActionListener
.Function
acima é uma interface funcional com apenas um método:apply
. É preciso um argumento e produz um resultado.A razão porque
::
s são impressionantes é que :Por exemplo, em vez de escrever o corpo lambda
Você pode simplesmente fazer
Em tempo de execução, esses dois
square
métodos se comportam exatamente da mesma forma que o outro. O bytecode pode ou não ser o mesmo (embora, para o caso acima, o mesmo bytecode seja gerado; compile o acima e verifique comjavap -c
).O único critério importante a satisfazer é: o método que você fornece deve ter uma assinatura semelhante ao método da interface funcional usada como referência de objeto .
O abaixo é ilegal:
square
espera um argumento e retorna adouble
. Oget
método em Fornecedor retorna um valor, mas não aceita um argumento. 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
accept
método no Consumidor recebe uma entrada, mas não retorna nada.Acima,
getRandom
não aceita argumentos e retorna adouble
. Portanto, qualquer interface funcional que atenda aos critérios de: não aceite argumentos e retornedouble
pode ser usada.Outro exemplo:
No caso de tipos parametrizados :
As referências de método podem ter estilos diferentes, mas, fundamentalmente, todas significam a mesma coisa e podem simplesmente ser visualizadas como lambdas:
ClassName::methName
)instanceRef::methName
)super::methName
)ClassName::methName
)ClassName::new
)TypeName[]::new
)Para referência adicional, consulte http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-final.html .
fonte
Sim, é verdade. O
::
operador é usado para referência ao método. Então, pode-se extrair estática métodos de classes usando-os ou métodos de objetos. O mesmo operador pode ser usado mesmo para construtores. Todos os casos mencionados aqui são exemplificados no exemplo de código abaixo.A documentação oficial da Oracle pode ser encontrada aqui .
Você pode ter uma visão geral melhor das alterações do JDK 8 neste artigo. Na seção de referência de método / construtor , também é fornecido um exemplo de código:
fonte
method(Math::max);
é invocação e definição de método seria semelhantepublic static void method(IntBinaryOperator op){System.out.println(op.applyAsInt(1, 2));}
. É assim que é usado.Parece um pouco tarde, mas aqui estão meus dois centavos. Uma expressão lambda é usada para criar métodos anônimos. Ele não faz nada além de chamar um método existente, mas é mais claro fazer referência ao método diretamente por 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.
Suponha que tenhamos uma lista de funcionários retornada por algum método e que desejemos classificar os funcionários por nota. Sabemos que podemos fazer uso da classe anônima como:
onde getDummyEmployee () é algum método como:
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 estáticos ou padrão). A expressão Lambda fornece implementação
@FunctionalInterface
para que uma interface funcional possa ter apenas um método abstrato. Podemos usar a expressão lambda como:Parece tudo de bom, mas e se a classe
Employee
também fornecer um método semelhante:Nesse caso, o uso do próprio nome do método será mais claro. Portanto, podemos nos referir diretamente ao método usando a referência do método como:
De acordo com os documentos, existem quatro tipos de referências de método:
fonte
::
é 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 é:
Para referenciar métodos não estáticos, a sintaxe é
E
O único pré-requisito para referenciar um método é que o método exista em uma interface funcional, que deve ser compatível com a referência do método.
As referências de método, quando avaliadas, criam uma instância da interface funcional.
Encontrado em: http://www.speakingcs.com/2014/08/method-references-in-java-8.html
fonte
Esta é uma referência de método no Java 8. A documentação do oracle está aqui .
Conforme declarado na documentação ...
fonte
:: 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:
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:
Para tornar o código mais claro, você pode transformar essa expressão lambda em uma referência de método:
fonte
O :: é conhecido como referências de método. Digamos que queremos chamar um método calculado da classe Purchase. Então podemos escrever como:
Também pode ser vista como uma forma curta de escrever a expressão lambda Como as referências do método são convertidas em expressões lambda.
fonte
Achei essa fonte muito interessante.
De fato, é o Lambda que se transforma em um cólon duplo . O cólon duplo é mais legível. Seguimos essas etapas:
PASSO 1:
PASSO 2:
ETAPA 3:
fonte
Person::getAge()
deveria serPerson::getAge
.return reduce(Math::max);
é NOT EQUAL parareturn reduce(max());
Mas isso significa, algo como isto:
Você pode salvar 47 pressionamentos de tecla se escrever assim
fonte
Como muitas respostas aqui explicam o bom
::
comportamento, além disso, gostaria de esclarecer que o::
operador não precisa ter exatamente a mesma assinatura que a 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:Como você vê na implementação anônima, ele requer dois argumentos TestObject e retorna um objeto TestObject também. Para satisfazer essa condição usando o
::
operador, podemos começar com um método estático:e depois chame:
Ok, compilou bem. E se precisarmos de um método de instância? Permite atualizar TestObject com o método de instância:
Agora podemos acessar a instância como abaixo:
Esse código compila bem, mas abaixo de um não:
Meu eclipse me diz "Não é possível fazer uma referência estática para o método não estático testInstance (TestObject, TestObject) do tipo TestObject ..."
É justo o método de instância, mas se sobrecarregarmos
testInstance
como abaixo:E ligue para:
O código será compilado corretamente. 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? Permite imprimir e ver:Qual saída:
Ok, então a JVM é inteligente o suficiente para chamar param1.testInstance (param2). Podemos usar a
testInstance
partir de outro recurso, mas não o TestObject, ou seja:E ligue para:
Ele não será compilado e o compilador dirá: "O tipo TestUtil não define testInstance (TestObject, TestObject)" . Portanto, o compilador procurará uma referência estática se não for do mesmo tipo. Ok, e o polimorfismo? Se removermos os modificadores finais e adicionarmos a classe SubTestObject :
E ligue para:
Ele não será compilado também, o compilador ainda procurará por referência estática. Mas o código abaixo será compilado, pois está passando no teste is-a:
fonte
No java-8, o Streams Reducer em trabalhos simples é uma função que recebe 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 disponível.
fonte
No tempo de execução, eles se comportam exatamente da mesma maneira.
No tempo de execução, eles se comportam exatamente do mesmo modo. (Math :: max) ;, gera a mesma matemática (cumpra acima e verifique javap -c;))
fonte
Nas versões Java mais antigas, em vez de "::" ou lambd, você pode usar:
Ou passando para o método:
fonte
Então , vejo aqui toneladas de respostas que são francamente super complicadas, e isso é um eufemismo.
A resposta é bem simples: :: é chamado de Referências de método https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html
Portanto, não copio e colo, no link, você pode encontrar todas as informações se rolar para baixo até a tabela.
Agora, vamos dar uma rápida olhada no que é uma referência de método:
A :: B substitui um pouco a seguinte expressão lambda embutida : (params ...) -> AB (params ...)
Para correlacionar isso com suas perguntas, é necessário entender uma expressão java lambda. O que não é difícil.
Uma expressão lambda embutida é semelhante a uma interface funcional definida (que é uma interface que não possui mais e não menos que 1 método) . Vamos dar uma olhada no que quero dizer:
InterfaceX deve ser uma interface funcional. Qualquer interface funcional, a única coisa importante sobre o InterfaceX para esse compilador é que você defina o formato:
InterfaceX pode ser qualquer um destes:
ou isto
ou mais genérico:
Vamos pegar o primeiro caso apresentado e a expressão lambda embutida que definimos anteriormente.
Antes do Java 8, você poderia defini-lo da seguinte maneira:
Funcionalmente, é a mesma coisa. A diferença está mais na maneira como o compilador percebe isso.
Agora que vimos a expressão lambda embutida, vamos retornar às referências de método (: :). Digamos que você tenha uma classe como esta:
Como o método anyFunctions possui os mesmos tipos que o InterfaceX callMe , podemos equivaler os dois a uma Referência de método.
Podemos escrever assim:
e isso é equivalente a isso:
Uma coisa interessante e a vantagem das referências de método são que, a princípio, até você atribuí-las a variáveis, elas não têm tipo. Assim, 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
fonte
As respostas anteriores são bastante completas sobre o que
::
referência de método faz. Para resumir, 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
ArrayList
WITH e SEM o uso da::
referência de método. As explicações estão nos comentários abaixo.SEM o uso de
::
Com o uso de
::
fonte
O cólon duplo, ou seja, o
::
operador é introduzido no Java 8 como uma referência de método . A referência de método é uma forma de expressão lambda usada para referenciar o método existente por seu nome.classname :: methodName
ex:-
stream.forEach(element -> System.out.println(element))
Usando Double Colon
::
stream.forEach(System.out::println(element))
fonte