Duas referências de método exatas não são iguais

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.