Eu tenho um método que é cerca de dez linhas de código. Quero criar mais métodos que façam exatamente a mesma coisa, exceto um pequeno cálculo que alterará uma linha de código. Este é um aplicativo perfeito para passar um ponteiro de função para substituir essa linha, mas o Java não possui ponteiros de função. Qual é a minha melhor alternativa?
java
closures
function-pointers
Bill the Lizard
fonte
fonte
::
operador, por outro lado ...Respostas:
Classe interna anônima
Digamos que você queira que uma função seja passada com um
String
parâmetro que retorne umint
.Primeiro, você precisa definir uma interface com a função como seu único membro, se não puder reutilizar uma existente.
Um método que pega o ponteiro apenas aceita a
StringFunction
instância da seguinte maneira:E seria chamado assim:
EDIT: No Java 8, você poderia chamá-lo com uma expressão lambda:
fonte
Para cada "ponteiro de função", eu criaria uma pequena classe de functor que implementa seu cálculo. Defina uma interface que todas as classes implementarão e passe instâncias desses objetos para sua função maior. Esta é uma combinação do " padrão de comando " e " padrão de estratégia ".
O exemplo do @ sblundy é bom.
fonte
Quando há um número predefinido de cálculos diferentes que você pode fazer nessa linha, o uso de uma enumeração é uma maneira rápida e clara de implementar um padrão de estratégia.
Obviamente, a declaração do método de estratégia, bem como exatamente uma instância de cada implementação, são definidas em uma única classe / arquivo.
fonte
Você precisa criar uma interface que forneça as funções que você deseja transmitir. por exemplo:
Então, quando você precisar passar uma função, poderá implementar essa interface:
Por fim, a função map usa o passado na Function1 da seguinte maneira:
Geralmente, você pode usar o Runnable em vez de sua própria interface, se não precisar passar parâmetros, ou pode usar várias outras técnicas para tornar a contagem de parâmetros menos "fixa", mas geralmente é uma troca com a segurança de tipo. (Ou você pode substituir o construtor do objeto de função para passar os parâmetros dessa maneira. Existem muitas abordagens e algumas funcionam melhor em determinadas circunstâncias.)
fonte
Referências de método usando o
::
operadorVocê pode usar referências de método em argumentos de método em que o método aceita uma interface funcional . Uma interface funcional é qualquer interface que contenha apenas um método abstrato. (Uma interface funcional pode conter um ou mais métodos padrão ou métodos estáticos.)
IntBinaryOperator
é uma interface funcional. Seu método abstratoapplyAsInt
, aceita doisint
s como parâmetros e retorna anint
.Math.max
também aceita dois seint
retorna umint
. Neste exemplo,A.method(Math::max);
makeparameter.applyAsInt
envia seus dois valores de entradaMath.max
e retorna o resultado dissoMath.max
.Em geral, você pode usar:
ao invés de:
que é a abreviação de:
Para obter mais informações, consulte Operador :: (dois pontos) no Java 8 e Java Language Specification §15.13 .
fonte
Você também pode fazer isso (o que em algumas ocasiões RARAS faz sentido). O problema (e é um grande problema) é que você perde toda a segurança de tipo ao usar uma classe / interface e precisa lidar com o caso em que o método não existe.
Ele tem o "benefício" de que você pode ignorar as restrições de acesso e chamar métodos privados (não mostrados no exemplo, mas pode chamar métodos que o compilador normalmente não deixaria chamar).
Novamente, é raro que isso faça sentido, mas nessas ocasiões é uma boa ferramenta.
fonte
Se você tiver apenas uma linha diferente, poderá adicionar um parâmetro como uma flag e uma instrução if (flag) que chame uma linha ou outra.
fonte
Você também pode estar interessado em saber sobre o trabalho em andamento no Java 7 envolvendo encerramentos:
Qual é o estado atual dos fechamentos em Java?
http://gafter.blogspot.com/2006/08/closures-for-java.html
http://tech.puredanger.com/java7/#closures
fonte
Novas interfaces funcionais do Java 8 e referências de método usando o
::
operador.O Java 8 é capaz de manter referências de método (MyClass :: new) com ponteiros " @ Functional Interface ". Não há necessidade do mesmo nome de método, apenas a mesma assinatura de método necessária.
Exemplo:
Então, o que temos aqui?
VOCÊ DEVE USAR INTERFACES FUNCIONAIS APENAS PARA OUVIR OUVIDOS POR ISSO!
Porque todos os outros indicadores de função são muito ruins para a legibilidade do código e a capacidade de entender. No entanto, referências diretas a métodos às vezes são úteis, com foreach, por exemplo.
Existem várias interfaces funcionais predefinidas:
Para versões anteriores do Java, tente o Guava Libraries, que possui funcionalidade e sintaxe semelhantes, como Adrian Petrescu mencionou acima.
Para pesquisas adicionais, consulte o Java 8 Cheatsheet
e obrigado a The Guy with The Hat pelo link §15.13 da especificação da linguagem Java .
fonte
A resposta do @ sblundy é ótima, mas as classes internas anônimas têm duas pequenas falhas, a principal é que elas tendem a não ser reutilizáveis e a secundária é uma sintaxe volumosa.
O bom é que o padrão dele se expande para classes completas sem nenhuma alteração na classe principal (aquela que executa os cálculos).
Quando você instancia uma nova classe, pode passar parâmetros para essa classe que podem atuar como constantes em sua equação - portanto, se uma de suas classes internas se parecer com isso:
mas às vezes você precisa de um que seja:
e talvez um terceiro que seja:
em vez de criar duas classes internas anônimas ou adicionar um parâmetro "passthrough", você pode criar uma única classe ACTUAL que instancia como:
Simplesmente armazenaria a constante na classe e a usaria no método especificado na interface.
De fato, se SABE que sua função não será armazenada / reutilizada, você poderá fazer o seguinte:
Mas classes imutáveis são mais seguras - não posso apresentar uma justificativa para tornar uma classe como essa mutável.
Eu realmente só posto isso porque me encolho sempre que ouço uma classe interna anônima - eu vi muitos códigos redundantes que eram "Necessários" porque a primeira coisa que o programador fez foi ficar anônimo quando ele deveria ter usado uma classe real e nunca repensou sua decisão.
fonte
As bibliotecas do Google Guava , que estão se tornando muito populares, têm um objeto genérico de Function and Predicate que eles trabalharam em várias partes de sua API.
fonte
Parece um padrão de estratégia para mim. Confira os padrões Java do fluffycat.com.
fonte
Uma das coisas que realmente sinto falta ao programar em Java é o retorno de chamada de função. Uma situação em que a necessidade delas continuava se apresentando era nas hierarquias de processamento recursivo, nas quais você deseja executar alguma ação específica para cada item. Como caminhar em uma árvore de diretórios ou processar uma estrutura de dados. O minimalista dentro de mim odeia ter que definir uma interface e depois uma implementação para cada caso específico.
Um dia eu me perguntei por que não? Temos ponteiros de método - o objeto Method. Com a otimização de compiladores JIT, a invocação reflexiva realmente não acarreta uma grande penalidade de desempenho. E além de, digamos, copiar um arquivo de um local para outro, o custo da invocação do método refletido é insignificante.
Enquanto pensava mais sobre isso, percebi que um retorno de chamada no paradigma OOP requer a vinculação de um objeto e um método - insira o objeto de retorno de chamada.
Confira minha solução baseada em reflexão para retornos de chamada em Java . Grátis para qualquer uso.
fonte
OK, este tópico já tem idade suficiente, então provavelmente minha resposta não é útil para a pergunta. Mas como esse tópico me ajudou a encontrar minha solução, eu o colocarei aqui de qualquer maneira.
Eu precisava usar um método estático variável com entrada e saída conhecidas (ambas duplas ). Então, sabendo o pacote e o nome do método, eu poderia trabalhar da seguinte maneira:
para uma função que aceita um duplo como parâmetro.
Então, na minha situação concreta, eu o inicializei com
e invocou-o mais tarde em uma situação mais complexa com
onde atividade é um valor duplo arbitrário. Estou pensando em talvez fazer isso um pouco mais abstrato e generalizá-lo, como o SoftwareMonkey fez, mas atualmente estou feliz o suficiente com o jeito que está. Três linhas de código, sem classes e interfaces necessárias, isso não é muito ruim.
fonte
code
remarcação, eu estava muito impaciente e estúpido para encontrá-lo ;-)Para fazer a mesma coisa sem interfaces para uma matriz de funções:
fonte
Confira lambdaj
http://code.google.com/p/lambdaj/
e, em particular, seu novo recurso de fechamento
http://code.google.com/p/lambdaj/wiki/Closures
e você encontrará uma maneira muito legível de definir o fechamento ou o ponteiro de função sem criar interface sem sentido ou usar classes internas feias
fonte
Uau, por que não apenas criar uma classe Delegate que não é tão difícil, já que eu já fiz para java e usá-la para passar no parâmetro onde T é o tipo de retorno. Sinto muito, mas como um programador de C ++ / C # em geral apenas aprendendo java, preciso de ponteiros de função porque eles são muito úteis. Se você estiver familiarizado com alguma classe que lida com informações de método, você pode fazê-lo. Nas bibliotecas java, isso seria java.lang.reflect.method.
Se você sempre usa uma interface, sempre precisa implementá-la. Na manipulação de eventos, não há realmente uma maneira melhor de registrar / cancelar o registro da lista de manipuladores, mas para os delegados onde você precisa passar as funções e não o tipo de valor, criando uma classe delegada para lidar com isso para ultrapassar uma interface.
fonte
Nenhuma das respostas do Java 8 deu um exemplo completo e coeso, então aqui está.
Declare o método que aceita o "ponteiro de função" da seguinte maneira:
Chame-o fornecendo à função uma expressão lambda:
fonte
Se alguém está lutando para passar uma função que usa um conjunto de parâmetros para definir seu comportamento, mas outro conjunto de parâmetros nos quais executar, como os do Scheme:
consulte Função de passagem com comportamento definido por parâmetro em Java
fonte
Desde o Java8, você pode usar lambdas, que também possuem bibliotecas na API oficial do SE 8.
Uso: Você precisa usar uma interface com apenas um método abstrato. Faça uma instância dele (você pode usar o java SE 8 já fornecido) como este:
Para obter mais informações, consulte a documentação: https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
fonte
Antes do Java 8, o substituto mais próximo da funcionalidade de ponteiro de função era uma classe anônima. Por exemplo:
Mas agora no Java 8, temos uma alternativa muito interessante, conhecida como expressão lambda , que pode ser usada como:
onde isBiggerThan é um método em
CustomClass
. Também podemos usar referências de método aqui:fonte