Interfaces funcionais - Java 8
Você já pensou em Interfaces Funcionais e como elas funcionam em Java? Vamos vê-las agora!
Primeiro de tudo, você sabe o que é uma Interface Funcional?
Interfaces funcionais são interfaces que têm um método a ser implementado, em outras palavras, um método abstrato. Isso significa que toda interface criada que respeite esta premissa, tornando-se automaticamente uma interface funcional.
O compilador reconhece essas interfaces e permite que elas estejam disponíveis para que os desenvolvedores trabalhem, por exemplo, com expressões lambda.
Hoje, vamos falar sobre as principais Interfaces Funcionais apresentadas no JDK, que são
- Fornecedor
- Consumidor e BiConsumidor
- Predicado e BiPredicado
- Função e BiFunção
- UnaryOperator e BinaryOperator
Fornecedor
Verificando sua classe, podemos ver isso abaixo:
![](https://cdn.prod.website-files.com/6475df10614983200b234b6b/64aed6ee770fdb4d5b65b117_6050a0428914776684f8e4ea_sE2CG4WzcJzHuu28bnR-mmf9-wRKpu8vl54XqvZULpum0HeHc1FfdS912rBXhB6PRJ2yM8kznzSy2l3QOQ1frf-lSTA-n9yiBuCMzGJdQepnSpfjkgdjjgOnmuHFmnHqQ8xWgh0k.webp)
O que podemos concluir com isso?
A letra T significa que é genérica (genérico significa que pode ser de qualquer tipo), e neste caso significa que a operação get( ), quando executada, vai devolver algo para nós e pode ser de qualquer tipo.
Por outro lado, não precisamos passar um argumento. Em resumo, chamamos isso e recebemos algo - como um fornecedor (você entendeu o nome agora?).
Vamos ver um exemplo:
![](https://cdn.prod.website-files.com/6475df10614983200b234b6b/64aed6ed7b99a39f122ee849_6050a0422ad1691f64aa8381_u6pxVAysY4FjFlTOHXFjJSCpws3IuQ7UoDOv1iL-snzTpCDk924laPGPN3s-Zv4v2AI4Uvo8eoxYe1nKfEQ8UxHtAoc-hyT2m_9TLxeq4gaOLQ4fXz3UoLoO3IV47tkQ_InALFBC.webp)
Aqui estamos chamando o método generate() do Stream API, que precisa de um Fornecedor para ser executado.
Assim, não estamos passando nada para o método usando os parênteses vazios '()', depois usando lambda '->' e finalmente executando o método - novo Random().nextInt() - que vai retornar algo para nós (neste caso, um número aleatório).
Aqui, nós acabamos de adicionar o limite e paraEach para mostrar o resultado no console.
Portanto, se executarmos o código, nós o obteremos:
![](https://cdn.prod.website-files.com/6475df10614983200b234b6b/64aed6eea8dddd36eaf1f752_6050a04258253a3edcdcef8a_j-2-lQyl7e0wNQMRTfCaT9orxZEdEBr0VTA_NiD_l-ELJEsoKcCi7CX35A9-cpXrNfNAV2r0hOD7SP0ixEq4n3FtXnluJpj3f0WqQlYceXdA5p0b2AlKrJfjB4G_HCmT4Po-1zCX.webp)
É assim que o Fornecedor funciona - não precisamos fornecer nada e recebemos uma resposta.
Consumidor e BiConsumidor
Vamos verificar sua classe:
![](https://cdn.prod.website-files.com/6475df10614983200b234b6b/64aed6eda8dddd36eaf1f735_6050a0432ad16980aaaa8382_7-GnspLmNw-hfE6pgx84n11phWC814okzIEfpT9kJYSTpqHD7QcO4mAqOPF5Pz8kNDj50g6zwRw3Yi2uzf6uWWq6c1w-VzMTAsj5OTjUpgoJoaj_dR-0c9qKucHLbhpGbI7S1irn.webp)
É uma interface simples, o oposto de Fornecedor. Ela recebe uma variável genérica, faz algo com ela e depois, não devolve nada.
Exemplo:
![Exemplo](https://cdn.prod.website-files.com/6475df10614983200b234b6b/64aed6eda1207c617a51eaca_6050a042f465ee158787d0f4_CLXr149EqJOLhZbUZAzm1ej-RY-2sxZ_kjCtc4-4h0SO4zdwTMfvj9Ie7Uh_eorOvYc5RKWp_kC7G27lqcgSSYOoD8q9_gjde2S6OpQuQgJNHdiAN-zbz-6G3W87I6xldhJoNwQV.webp)
Temos uma lista da Integers e para imprimir estes números no console, podemos usar o .forEach para nos ajudar com isso.
Ele está recebendo uma variável - número - e fazendo algo com ela. Neste caso, ela está imprimindo no console e não devolvendo nada para o usuário.
Assim como um consumidor. Recebe algo, faz algo e é tudo.
O BiConsumer segue as mesmas regras, mas recebe dois argumentos.
![Exemplo](https://cdn.prod.website-files.com/6475df10614983200b234b6b/64aed6eecd9aa1cce4db0f9e_6050a0431de5c8b8c56d7dfa_JJZwYOENGfwGKHPCqcN0aN7_vRmBu_frqxRRHIypxU9ADJzz1B-rq4nD8Pmh5i_C9aGoFJRrfaPFFqAzy3BrI7DvRJpsdD16Z7_7-RErftfxJtRgfYmIvEzCW8-W3PEzyUdMkNuT.webp)
No exemplo acima, estamos recebendo dois valores inteiros e apenas imprimindo-os no console e devolvendo nada.
Predicado e BiPredicado
Agora, vamos dar uma olhada nas classes Predicado e BiPredicado:
![Exemplo](https://cdn.prod.website-files.com/6475df10614983200b234b6b/64aed6eea8b48672ca38f82b_6050a04398a8af99ccea84d7_21VGFYbOLwUZ0gOhbaZujR0JqCO6EDZLRXD_O3wy__NpHuLgxhIfiiuDZIabR3Vz2aHe1Zxwkm_mSW9gQavxeozeP3U0qGaO_RGHJRSQGtrNXbizbxDAyOW0r0k0DOY3pFiQzKRT.webp)
A classe Predicado tem um método chamado teste, que recebe um argumento, e retorna um booleano.
Podemos concluir que este método é utilizado para validar hipóteses. Vamos ver no código:
![](https://cdn.prod.website-files.com/6475df10614983200b234b6b/64aed6eed00efb0a6df65621_6050a04318454b60537778db_MpZbkn4saT9YyKvzU1cKTE7AvojqJrMBDZAxxDidqmkpSTdOeRG2-8FXefO6Bf1YAOqW_gZgcofUxhhKauchX0EiWkIi7KxIvsN-jacbJI25StD4ggUETPuqbpmMCDVEnJ8R-t0Z.webp)
Para este exemplo, temos uma Lista de Integers que é composta com os números - 1, 2, 3, 4 e 5.
E mais uma vez, vamos utilizar o Stream API. Vamos converter nossa lista para um Stream através do .stream() - então vamos usar o .filter() - e é neste pequenote que a magia acontece com o Predicado.
Nosso filtro de método precisa de um Predicado para ser executado e, como vimos antes, ele validará uma hipótese e retornará Verdadeiro ou Falso para nós.
![](https://cdn.prod.website-files.com/6475df10614983200b234b6b/64aed6f02d36048f55a0ad1b_6050a043531d8762883ea17a_XghQdVWDPeRQuLul2F9lYyDCCEtm822vtBl54zkCRsBi9s5aEBGs3hMflYZEmQ4t5nWg4-1RoZQuyD_M5qugiqJ2bqNH7tcAzHqhOa48PT-wGh7SgUd8Sit9Qjlpdf_mBzCsOX-6.webp)
Neste método, estamos obtendo o número e verificando se ele é divisível por 2 - o que significa que é um número par. Se for verdade, então executaremos o forEach, caso contrário, ele será ignorado.
É assim que os métodos Predicate funcionam: eles testam hipóteses e retornam a você se forem verdadeiros ou falsos.
Se executarmos este código, obtemos este resultado:
![Exemplo](https://cdn.prod.website-files.com/6475df10614983200b234b6b/64aed6ef2d36048f55a0abde_6050a043493d09819c18a7b1_n-NOXwYQL0ICLir5zZfrT4U6wiAiQcc0zvCRLZkfWvkmwmHekdhPkCM4yDFfL9AQbwUKrdmQtUVNYYb00KbrWQKjH5Xa0VDfHJfNozuqYMsLAKyCodNP3nikkCpDKA27sKvh93So.webp)
Acabou de imprimir os números pares, como esperado.
E em relação ao BiPredicate, temos o mesmo comportamento, a única diferença é que vamos receber dois parâmetros a serem verificados em vez de um. Confira abaixo:
![](https://cdn.prod.website-files.com/6475df10614983200b234b6b/64aed6ee3c131b7d073d93b8_6050a043bf3c1c7bde37c875_Pd93FpjWGXcdGx12_fxIT7vG5UL9H5XuADJ3V_ykfYvG2lA6ga18yFXjxUT_xRjZJHnQcpwsrWCcoqF7lu6BI-oyqyzFcAXVxxWcIdAD0t6f4OJYV5Cp0rn1-NXgShqim3ORICAu.webp)
Exemplo:
![Exemplo](https://cdn.prod.website-files.com/6475df10614983200b234b6b/64aed6ee181ca798c2fb478b_6050a0430859b07e0c6d960f_h_CmQ2If9mGUyRW8v_qCv9bP4-yDDSlBFx4Jku5InMmEhmS5UlrbDX5GVhDB8El6MLYbXSSrvGdljrn92MStfWxsZnak29Hk0WcjKirH9sEvttrgojCMxKxyCwbbMVJXQyctlBKI.webp)
Aqui temos um BiPredicado recebendo dois parâmetros - palavra e tamanho - e verificando se eles têm o mesmo valor.
No primeiro teste, vamos receber o Verdadeiro, e no segundo, vamos receber o Falso.
Função e BiFunção
A função da interface funcional é a mais genérica. Ela tem a definição mais básica de uma função - ela recebe algo e retorna algo.
Vamos dar uma olhada na classe:
![Exemplo](https://cdn.prod.website-files.com/6475df10614983200b234b6b/64aed6efcd9aa1cce4db1032_6050a04361294e6318307bec_rTaWyqtBiVgyM-Jy4PzlRAkr6jTU7etGhcGcu4mkuE8nfe7GdaWtNADdVz3fbHreDUiSXuozmGSlr8RoarCvimJ5ySw0Vet989_t6LaNf_KnshWDbsZXo86QFA4TJBSEoLAtv2Sw.webp)
Vamos começar com a Função. Aqui podemos ver que o método aplicado recebe uma variável genérica - lembre-se que genérica significa que pode ser de qualquer tipo - e retornará outra variável genérica.
Uma coisa importante aqui é que mesmo que as palavras genéricas sejam diferentes - T e R - isso não significa que o método apply() não possa receber e devolver o mesmo tipo.
Então, vamos ao código - Mais uma vez, vamos usar o Stream API para este exemplo.
![](https://cdn.prod.website-files.com/6475df10614983200b234b6b/64aed6ee5db3fc5f5bf498a7_6050a0432ad169e91baa8383_OxbNxELaWOHHENDqUmW_kDxu56dUY0MsrNuYRhHFco5RlVAS_qoyDOy-r8MoOTpgQQ0Tp9xRBP1C2NwEOY0-CqL4mX_hIhTWxKa_eKhUO9hOBcPDpZWrJMPvAW4VQaWFI5d_5MAW.webp)
Aqui vamos usar o método .map(), que precisa de uma Função para ser executada.
Portanto, neste código:
![Exemplo](https://cdn.prod.website-files.com/6475df10614983200b234b6b/64aed6eea8dddd36eaf1f787_6050a0435a06653ca81f7aa8_sUFDJb4omRJtw3QKuPLRozhXRNsifBRerAo5TbT4j_NQ2CUvxCr5XNTfGr9NCFPzsWrZPCCQowMQymjr3YyGXLzpQ5N2_uLvbQl70hlZol25tuVXdU8Yc2b1SZSukcgmfMrEK0dB.webp)
Estamos recebendo o número, que é um valor Inteiro, e devolvendo um Duplo. Portanto, estamos passando um argumento para o mapa e recebendo uma resposta.
*Neste caso, estamos dando um valor Inteiro e recebendo como resposta um valor Duplo.
Mas, por exemplo, nós poderíamos fazer isso:
![Exemplo](https://cdn.prod.website-files.com/6475df10614983200b234b6b/64aed6eed00efb0a6df65630_6050a0441de5c8c4a66d7e02_h-Fcfv4p4Ti0YfoBnm_cHALdenzEuP36_ZfKU8IiteKGAq-b1kFIxOXsUEi0WSNDiCtVnvNQKmT6Mh-HnYZMIeKBMLkNosSLbpxxw3n2Ja7O2CZk3VQRxouylE2jz5cIX8TuPQ9M.webp)
É um valor Inteiro como argumento e seu retorno é um valor Inteiro também. A função permite isso. Com relação à BiFunção, ela tem o mesmo comportamento, exceto que recebe dois argumentos assim como o BiPredicado.
![Exemplo](https://cdn.prod.website-files.com/6475df10614983200b234b6b/64aed6ee5db3fc5f5bf498d8_6050a0448f3501a36bb0d219_qA0oL2MKVlDm8OWCOw-oaNmxqUMKFcbnRDFFY57tPX_bLIkl7cgCS_49frRHaurR7f0h7645aRFbGJsc4ltYUJ5qAXZO4lIQQrQCxF_94evy64UPT8MWZSF5RIOI_kN-vz31eU3C.webp)
Vamos verificar nos exemplos abaixo:
![Exemplo](https://cdn.prod.website-files.com/6475df10614983200b234b6b/64aed6efa8dddd36eaf1f7e0_6050a044fe7b0ea9c933f1b7_-hFK0_BXnCvhETAOiEuo2WH-EsE7GGNOwpoO_WaljDTzVOJHbnCxh3Zxy0Lt1G9jEAyThVfc6YaFjH6miyvdmgzoSfGlg_5pR6fXqCYXbeBHnDS-mhmhloc6iY5UraqaPf985krW.webp)
No primeiro exemplo (sumNumbers), temos uma BiFunção que recebe dois argumentos - tipo Inteiro - e retorna também um tipo Inteiro. Estamos fazendo uma soma e quando usamos o apply() resulta no número 3 (1 + 2).
A seguir, temos outro exemplo, mas, desta vez, ele está devolvendo um valor duplo. Obtivemos 2 números inteiros e, depois de chamar o Math.pow(), ele retornou um valor Duplo. Uma vez executado o método apply(), obteremos o resultado: 4.0 (Duplo)
Resumindo - damos um ou dois argumentos e recebemos algo em troca. O significado básico de uma função.
UnaryOperator e BinaryOperator
Vamos dar uma olhada em sua classe:
![Exemplo](https://cdn.prod.website-files.com/6475df10614983200b234b6b/64aed6ef01e19aa2444b60c4_6050a0441de5c838d86d7e03_kfymlkhh0Fq5CYCzn1-_4eK0dreOaWMILjJd1maU82n-P7nb_WQLwyacAj8J_WPOElbMf8G-1Z8QG6Ey-A-IWqX_gHh7_FirEYdcK1wMGSKWj7GeuJE5RBXayc-iS47W4f6TH5du.webp)
O UnaryOperator estende uma função - mas em seu construtor está definido o mesmo tipo de argumentos, você notou isso?
We have UnaryOperator <T> extending to Function <T, T>.
Está definindo o tipo permitido para esta interface, e como todos estes genéricos são a mesma letra (T), isso significa que só nos é permitido trabalhar com o mesmo tipo de variáveis - nossos argumentos e retornos devem ser do mesmo tipo para trabalhar no UnaryOperator.
UnaryOperator tem o mesmo comportamento que a Função, mas só funciona com os mesmos tipos.
Por exemplo:
![Exemplo](https://cdn.prod.website-files.com/6475df10614983200b234b6b/64aed6efd00efb0a6df6564d_6050a044fe7b0e98e733f1b8_bKLb_BzaECPhlPXxI60i5eaarXEfsF-RKjVC7UlsQ31tBNZt8_wE3ajd-2ZSLRdrFPzNUfTIbO01I4zBuXil1ZGpWdJs8TKyje0XoI1eESwrE3NkqFMK7L68FXuhmD9u1Hn6meJ2.webp)
Você pode apenas definir um tipo em seu construtor, e ele será estendido à Função, e como podemos ver no código acima, podemos apenas trabalhar com variáveis do mesmo tipo.
É a mesma coisa que as outras.
![](https://cdn.prod.website-files.com/6475df10614983200b234b6b/64aed6efa8b48672ca38fb5b_6050a0446bd44f4a815a53de_LbSNkVVgO3ofCkVci0ThPs8BPkEwk2IolmOVs9UypgprvMmYZcgeN0SjwP8SZ5iC9Snmwv2TwKGsbWmTvLI8uQRK0m3aoZB4tFaJPQpFcA5iGbv0mpa6coWNfY_smEwcK6tyH6au.webp)
Ela se estende à BiFunction. Portanto, vamos receber dois argumentos e devolver um - todos com o mesmo tipo.
Vamos checar isto:
![Exemplo](https://cdn.prod.website-files.com/6475df10614983200b234b6b/64aed6efa8dddd36eaf1f7ff_6050a044a6a63382d34ac809_8jqCXCwO4NVmkF_qGWnXax-Nvgeb_ayx_PWktVFWXTilOMD7ZTM1xXZi3pGreMnnwux62gkztCiFw14g5haYo30NhrK5MrMWupyAaW-tPtDU0ytQCkU7KvHMTHRCAYCkpGM8ZV0x.webp)
Usamos novamente o Stream API.
Agora, vamos usar o método .reduce(). Ele receberá dois argumentos e retornará um valor com o mesmo tipo.
Em nosso caso, temos um conjunto de números - 1 a 5 - e vamos somar todos os valores.
*Como reduzir os retornos um valor Opcional, vamos usar o .ifPresent() para imprimir nosso resultado.
Portanto, como resultado, teremos aqui: 15 (1 + 2 + 3 + 4 + 5)
*Vai se repetir até que toda a matriz passe por ela.
Para embrulhar as coisas, vejamos um exemplo de tudo trabalhando em conjunto:
![](https://cdn.prod.website-files.com/6475df10614983200b234b6b/64aed6f03c131b7d073d9523_6050a0442440401ef5917564_D2m1XY-5m068_EV1JtJd-c6KQTBCuGJwlFO5udzF4FrsD9w6OrGogsGoKAN4S6fOnOPIUxhX0WsLDRNJU80CXXfXHruMr0wdYrjlNuhXZnOIZ237wm1TlgxxsPtyQIyY46FFuohH.webp)
- Aqui estamos obtendo apenas os números pares;
- Em seguida, transformando-os em Duplo;
- Em seguida, somando todas elas;
- E se houver um número no final, nós o imprimimos no console.
Apenas uma última coisa - as interfaces funcionais também aceitam Referência de Método - o código poderia ser assim:
![Exemplo](https://cdn.prod.website-files.com/6475df10614983200b234b6b/64aed6f0181ca798c2fb487b_6050a0458f3501779eb0d21b_KRtSrszUn-MAAbF88UbaKDnNmW7LNzJ8WAkqjEImtESfjhEagq7kWsrUfOvlpHe8z7TkXkMJx1tB5h8uU_6evWGM8Rh9GpzQof01gOfidVqK963dJnYtrsItL2xmGBcOp8WKErmV.webp)
É isso aí! Espero que isso possa ajudá-lo a criar um código melhor e mais limpo! Assim como ajudar você a entender como funcionam as funções.
Inicie sua jornada conosco
Estamos prontos para guiar o seu negócio rumo ao futuro, com a solução certa para você se beneficiar do potencial das APIs e integrações modernas.
Conteúdos relacionados
Confira os conteúdos produzidos pela nossa equipe
Sua história de sucesso começa aqui
Conte com nosso apoio para levar as melhores integrações para o seu negócio, com soluções e equipes profissionais que são referência no mercado.