O teste a seguir falha
@Test public void test() { Function foo = Integer::parseInt; Function bar = Integer::parseInt; assertThat(foo, equalTo(bar)); }
Existe alguma maneira de fazer isso passar?
edit : Vou tentar deixar mais claro o que estou tentando fazer.
Vamos dizer que eu tenho essas classs:
class A { public int foo(Function foo) {...} } class B { private final A a; // c'tor injected public int bar() { return a.foo(Integer::parseInt); } }
Agora vamos dizer que eu quero escrever teste de unidade para B:
@Test public void test() { A a = mock(A.class); B b = new B(a); b.bar(); verify(a).foo(Integer::parseInt); }
o problema é que o teste falha, porque as referências do método não são iguais.
Lambdas não são armazenados em cache e isso parece ser deliberado. Não há como comparar dois lambdas para ver se eles fariam o mesmo.
Você precisa fazer algo como
static final Function parseInt = Integer::parseInt; @Test public void test() { Function foo = parseInt; Function bar = parseInt; assertThat(foo, equalTo(bar)); }
Resposta de Brian Goetz; Existe uma maneira de comparar lambdas?
Eu não tenho a API na mão, mas a function é uma interface. Integer :: parseInt parece não armazenar em cache, então retornará duas instâncias diferentes, que serão comparadas por referência => false.
Você pode fazer isso passando escrevendo um Comparador, que faz o que você quer.
Veja a especificação da linguagem Java:
15.27.4. Avaliação em Tempo de Execução de Expressões Lambda
No tempo de execução, a avaliação de uma expressão lambda é semelhante à avaliação de uma expressão de criação de instância de class, na medida em que a conclusão normal produz uma referência a um object. A avaliação de uma expressão lambda é distinta da execução do corpo lambda.
Uma nova instância de uma class com as propriedades abaixo é alocada e inicializada ou uma instância existente de uma class com as propriedades abaixo é referenciada.
…
Essas regras são destinadas a oferecer flexibilidade para implementações da linguagem de programação Java, na medida em que:
Um novo object não precisa ser alocado em todas as avaliações.
Objetos produzidos por diferentes expressões lambda não precisam pertencer a classs diferentes (se os corpos são idênticos, por exemplo).
Todo object produzido por avaliação não precisa pertencer à mesma class (variables locais capturadas podem ser embutidas, por exemplo).
Se uma “instância existente” estiver disponível, ela não precisa ter sido criada em uma avaliação lambda anterior (ela pode ter sido alocada durante a boot da class envolvente, por exemplo).
Em princípio, isso implica que mesmo uma única ocorrência de Integer::parseInt
em seu código-fonte pode levar a instâncias de object diferentes (mesmo de classs diferentes) ao ser avaliada várias vezes, sem falar em várias ocorrências dela. A decisão exata é deixada para a implementação real do JRE. Veja esta resposta discutindo o comportamento atual da implementação da Oracle.
Tudo bem que o teste não passe. Lambdas não são objects, eles não estão sujeitos a propriedades como a identidade do object. Em vez disso, são implementações ad hoc de interfaces funcionais.
Eu acredito que você não deveria esperar que seu código contasse com o comportamento que você descreveu.