Dificuldade para entender como funciona as funções callback em java

Saudações caros colegas programadores

Eu estou fazendo este topico pois estou com algumas dificuldades para entender como funcionam as callbacks em java.

Como estou fazendo o meu tcc do técnico em android studio, muitas, se não todas as funções que existem nele para manipulação de dados envolvem callbacks (ou classe anonima do java?) e a forma que elas são escritas tá dando um nó na minha cabeça

O codigo proposto pelo exercicio que eu estou fazendo é o seguinte:

private EditText caixaTexto;
private Button botaoIdade;
private TextView resultadoIdade;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    caixaTexto = (EditText) findViewById(R.id.txt_idade);
    botaoIdade = (Button) findViewById(R.id.btn_Calcular);
    resultadoIdade = (TextView) findViewById(R.id.lbl_Resultado);

    botaoIdade.setOnClickListener(new View.OnClickListener(){
        @Override
        public void onClick(View v){
            // Recupera dados quando for clicado
            String textoDigitado = caixaTexto.getText().toString();

            if (textoDigitado.isEmpty()){
                resultadoIdade.setText("Nenhum numero digitado");

            }else{
                int valorDitigado = Integer.parseInt(textoDigitado);
                int idadeFinal = valorDitigado * 7;

                resultadoIdade.setText("A idade do cachorro em anos humanos é equivalente a: " + idadeFinal + " anos");

A parte que eu estou tendo mais dificuldade para entender é a:

botaoIdade.setOnClickListener(**new View.OnClickListener**()

E a :

 public void onClick(View v)

No primeiro exemplo, eu não entendo como que o objeto pode estar sendo instanciado sem uma referência.

Já no segundo bloco de codigo eu simplesmente não estou conseguindo entender a referência View v. De onde ela surge, por que ela surge?

Callbacks (ou Listeners, ou Observers) não são necessariamente classes anônimas, no seu código você optou por usar classe anônima.

Uma classe anônima, como o nome sugere, é uma classe que não possui nome, basicamente você usa a instrução new seguida do nome da interface que sua classe anônima vai implementar ou o nome da classe que ela vai estender.

Objetos não são instanciados, eles já são a instância, um objeto é a instância de uma classe.
No seu exemplo, você já está passando o retorno da instrução new para o método setOnClickListener.

Você implementou seu listener dessa forma:

botaoIdade.setOnClickListener(new View.OnClickListener(){

    @Override
    public void onClick(View v){
        // Recupera dados quando for clicado
        String textoDigitado = caixaTexto.getText().toString();
        if (textoDigitado.isEmpty()){
            resultadoIdade.setText("Nenhum numero digitado");
        }else{
            int valorDitigado = Integer.parseInt(textoDigitado);
            int idadeFinal = valorDitigado * 7;
            resultadoIdade.setText("A idade do cachorro em anos humanos é equivalente a: " + idadeFinal + " anos");
        }
    }
});

Mas poderia ter feito assim:

View.OnClickListener referencia = new View.OnClickListener(){

    @Override
    public void onClick(View v){
        // Recupera dados quando for clicado
        String textoDigitado = caixaTexto.getText().toString();
        if (textoDigitado.isEmpty()){
            resultadoIdade.setText("Nenhum numero digitado");
        }else{
            int valorDitigado = Integer.parseInt(textoDigitado);
            int idadeFinal = valorDitigado * 7;
            resultadoIdade.setText("A idade do cachorro em anos humanos é equivalente a: " + idadeFinal + " anos");
        }
    }
};
botaoIdade.setOnClickListener(referencia);

Ou assim:

class MeuListener implements View.OnClickListener {

    @Override
    public void onClick(View v){
        // Recupera dados quando for clicado
        String textoDigitado = caixaTexto.getText().toString();
        if (textoDigitado.isEmpty()){
            resultadoIdade.setText("Nenhum numero digitado");
        }else{
            int valorDitigado = Integer.parseInt(textoDigitado);
            int idadeFinal = valorDitigado * 7;
            resultadoIdade.setText("A idade do cachorro em anos humanos é equivalente a: " + idadeFinal + " anos");
        }
    }
};
botaoIdade.setOnClickListener(new MeuListener());

Se você olhar a documentação da interface View.OnClickListener, vai descobrir que ela só define um método, o public void onClick(View v), que é chamado quando a View recebe um clique.
No seu exemplo, essa View é o botaoIdade.

Se está confuso para entender como esse mecanismo funciona, sugiro que pesquise/estude o padrão de projeto Observer, também conhecido como Listener.

4 curtidas

A explicação do primeiro argumento eu entendi bem, fui inclusive atrás do padrão Observer, entendi a função dele, estou ainda com duvidas quanto a execução, nada que umas praticas boas não resolvam…

…Mas eu ainda estou com uma baita duvida de como a View interpreta sem nenhuma referência que a View que eu estou tratando é o botaoIdade, e ainda continuo com a duvida do por que eu apenas passo uma referência pro onClick(View v), ao invés de um objeto, ou uma classe anônima…

Pelo que eu estudei de java até agora(confesso que foi muito pouco), esse padrão de parâmetro não me faz o menor sentido hahaha.

Tipo classes não foram feitas para terem objetos? Como eu posso ter apenas uma referência a ela dentro de um argumento?

Mas você passa uma referência para o botão quando você faz o botaoIdade.setOnClickListener.
Você passa uma referência pra um objeto de uma classe anônima que implementa View.OnClickListener e esse objeto tem um método onClick(View v).
Quando o botão é pressionado ele vai chamar o método onClick passando a si mesmo como parâmetro.

Aparentemente você ainda não compreendeu como funciona um listener, tenta compilar e executar esse exemplo abaixo:

Uma classe hipotética Botao:

public class Botao {

    public String descricao;

    private BotaoListener listener;

    public Botao(String descricao) {
        this.descricao = descricao;
    }

    public void setListener(BotaoListener listener) {
        this.listener = listener; // o botão é que guarda uma referência pro listener
    }

    public void clicar() {
        if (listener != null) { // tem um listener registrado?
            listener.clicou(this); // tem, então notifica ele de que este botão foi clicado
        }
    }
}

Um listener para tratar os cliques do Botao:

public interface BotaoListener {

    public void clicou(Botao botao); // o botão chama este método quando ele é clicado
}

Uma classe de exemplo:

public class Exemplo {

    public static void main(String[] args) {
        // criar o botão 1
        Botao botao1 = new Botao("Botão 1");

        // registrar um listener pro botão 1
        botao1.setListener(new BotaoListener() {  // classe anonima que implementa BotaoListener

            @Override
            public void clicou(Botao botao) {
                System.out.println("Listener 1: clicou em " + botao.descricao);
            }
        });

        // criar o botão A
        Botao botaoA = new Botao("Botão A");

        // registrar um listener pro botão A
        botaoA.setListener(new BotaoListener() { // classe anonima que implementa BotaoListener

            @Override
            public void clicou(Botao botao) {
                System.out.println("Listener a: clicou em " + botao.descricao);
            }
        });

        // agora vamos clicar nos botões e ver o que acontece
        botao1.clicar();
        botaoA.clicar();
    }
}