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:
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 retorna um objeto TestObject também. Para satisfazer essa condição usando o ::
operador, 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 chame:
BinaryOperator<TestObject> binary = TestObject::testStatic;
Ok, compilou bem. E se precisarmos de um método de instância? Permite atualizar TestObject com o método de 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;
Esse 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 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:
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 para:
BinaryOperator<TestObject> binary = TestObject::testInstance;
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:
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 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 a testInstance
partir de outro recurso, mas não o TestObject, ou seja:
public class TestUtil {
public final TestObject testInstance(TestObject t){
return t;
}
}
E ligue para:
BinaryOperator<TestObject> binary = TestUtil::testInstance;
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 :
public class SubTestObject extends TestObject {
public final TestObject testInstance(TestObject t){
return t;
}
}
E ligue para:
BinaryOperator<TestObject> binary = SubTestObject::testInstance;
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:
public class TestObject {
public SubTestObject testInstance(Object t){
return (SubTestObject) t;
}
}
BinaryOperator<TestObject> binary = TestObject::testInstance;
* Estou apenas estudando, então descobri, tente e veja, sinta-se à vontade para me corrigir se estiver errado