Pergunta

Eu tenho um método que é sobre linhas dez de código. Eu quero criar mais métodos que fazem exatamente a mesma coisa, exceto por um pequeno cálculo que vai mudar uma linha de código. Este é um aplicativo perfeito para passar em um ponteiro de função para substituir que uma linha, mas Java não tem ponteiros de função. Qual é a minha melhor alternativa?

Foi útil?

Solução

Classe interna anônima

Digamos que você queira ter uma função passada com um param String que retorna um int.
Primeiro você tem que definir uma interface com a função como seu único membro, se você não pode reutilizar uma já existente.

interface StringFunction {
    int func(String param);
}

Um método que leva o ponteiro seria apenas aceitar exemplo StringFunction assim:

public void takingMethod(StringFunction sf) {
   int i = sf.func("my string");
   // do whatever ...
}

E seria chamado assim:

ref.takingMethod(new StringFunction() {
    public int func(String param) {
        // body
    }
});

EDIT: Em Java 8, você poderia chamá-lo com uma expressão lambda:

ref.takingMethod(param -> bodyExpression);

Outras dicas

Para cada "ponteiro de função", eu criar um pequeno classe functor que implementa o seu cálculo. Definir uma interface que todas as classes irá implementar, e passar instâncias desses objetos em sua função maior. Esta é uma combinação do " comando de padrão ", e "strategy padrão ".

@ do sblundy exemplo é bom.

Quando há um número pré-definido de diferentes cálculos que você pode fazer em que uma linha, usando um enum é um rápido, ainda forma clara para implementar um padrão de estratégia.

public enum Operation {
    PLUS {
        public double calc(double a, double b) {
            return a + b;
        }
    },
    TIMES {
        public double calc(double a, double b) {
            return a * b;
        }
    }
     ...

     public abstract double calc(double a, double b);
}

Obviamente, a declaração de método estratégia, bem como exatamente uma instância de cada aplicação são todos definidos em uma única classe / arquivo.

Você precisa criar uma interface que fornece a função (s) que deseja passar ao redor. por exemplo:

/**
 * A simple interface to wrap up a function of one argument.
 * 
 * @author rcreswick
 *
 */
public interface Function1<S, T> {

   /**
    * Evaluates this function on it's arguments.
    * 
    * @param a The first argument.
    * @return The result.
    */
   public S eval(T a);

}

Então, quando você precisa para passar uma função, você pode implementar essa interface:

List<Integer> result = CollectionUtilities.map(list,
        new Function1<Integer, Integer>() {
           @Override
           public Integer eval(Integer a) {
              return a * a;
           }
        });

Finalmente, a função de mapa usa o passado em Function1 da seguinte forma:

   public static <K,R,S,T> Map<K, R> zipWith(Function2<R,S,T> fn, 
         Map<K, S> m1, Map<K, T> m2, Map<K, R> results){
      Set<K> keySet = new HashSet<K>();
      keySet.addAll(m1.keySet());
      keySet.addAll(m2.keySet());

      results.clear();

      for (K key : keySet) {
         results.put(key, fn.eval(m1.get(key), m2.get(key)));
      }
      return results;
   }

Muitas vezes você pode usar Runnable em vez de sua própria interface, se você não precisa passar parâmetros, ou você pode usar várias outras técnicas para fazer a contagem param menos "fixo", mas é geralmente um-trade off com segurança de tipo . (Ou você pode substituir o construtor para o objeto de função para passar nos parâmetros dessa maneira .. há muitas abordagens, e alguns funcionam melhor em determinadas circunstâncias.)

Referências a métodos que utilizam o operador ::

Você pode usar referências de métodos em argumentos de método onde o método aceita um interface funcional . Uma interface funcional é qualquer interface que contém apenas um método abstrato. (A interface funcional pode conter um ou mais métodos padrão ou métodos estáticos.)

IntBinaryOperator é um funcional interface. Seu método abstrato, applyAsInt , aceita dois ints como seus parâmetros e retorna um int. Math.max também aceita dois ints e retorna um int. Neste exemplo, A.method(Math::max); faz parameter.applyAsInt enviar seus dois valores de entrada para Math.max e retornar o resultado de que Math.max.

import java.util.function.IntBinaryOperator;

class A {
    static void method(IntBinaryOperator parameter) {
        int i = parameter.applyAsInt(7315, 89163);
        System.out.println(i);
    }
}
import java.lang.Math;

class B {
    public static void main(String[] args) {
        A.method(Math::max);
    }
}

Em geral, você pode usar:

method1(Class1::method2);

em vez de:

method1((arg1, arg2) -> Class1.method2(arg1, arg2));

que é curto para:

method1(new Interface1() {
    int method1(int arg1, int arg2) {
        return Class1.method2(arg1, agr2);
    }
});

Para obter mais informações, consulte (duplo dois pontos) em Java 8 e linguagem Java Specification §15.13 .

Você também pode fazer isso (que em alguns raras ocasiões faz sentido). A questão (e é um grande problema) é que você perde todo o typesafety de usar uma classe / interface e você tem que lidar com o caso em que o método não existe.

Ele tem o "benefício" que você pode ignorar as restrições de acesso e chamar métodos privados (não mostrado no exemplo, mas você pode chamar métodos que o compilador normalmente não iria deixá-lo chamar).

Mais uma vez, é um caso raro que isso faz sentido, mas nessas ocasiões, é uma boa ferramenta para ter.

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

class Main
{
    public static void main(final String[] argv)
        throws NoSuchMethodException,
               IllegalAccessException,
               IllegalArgumentException,
               InvocationTargetException
    {
        final String methodName;
        final Method method;
        final Main   main;

        main = new Main();

        if(argv.length == 0)
        {
            methodName = "foo";
        }
        else
        {
            methodName = "bar";
        }

        method = Main.class.getDeclaredMethod(methodName, int.class);

        main.car(method, 42);
    }

    private void foo(final int x)
    {
        System.out.println("foo: " + x);
    }

    private void bar(final int x)
    {
        System.out.println("bar: " + x);
    }

    private void car(final Method method,
                     final int    val)
        throws IllegalAccessException,
               IllegalArgumentException,
               InvocationTargetException
    {
        method.invoke(this, val);
    }
}

Se você tem apenas uma linha que é diferente você pode adicionar um parâmetro como uma bandeira e uma declaração if (flag) que chama uma linha ou de outra.

Você pode também estar interessado em ouvir sobre o trabalho em curso para Java 7 envolvendo fechamento:

Qual é o estado atual de encerramentos em Java?

http://gafter.blogspot.com/2006/08 /closures-for-java.html
http://tech.puredanger.com/java7/#closures

New Java 8 Interfaces funcionais e Método Referências usando o operador ::.

Java 8 é capaz de manter referências de método (MyClass :: novos) com " @ interface funcional " ponteiros. Não há necessidade de mesmo nome método, apenas mesmo método assinatura requerida.

Exemplo:

@FunctionalInterface
interface CallbackHandler{
    public void onClick();
}

public class MyClass{
    public void doClick1(){System.out.println("doClick1");;}
    public void doClick2(){System.out.println("doClick2");}
    public CallbackHandler mClickListener = this::doClick;

    public static void main(String[] args) {
        MyClass myObjectInstance = new MyClass();
        CallbackHandler pointer = myObjectInstance::doClick1;
        Runnable pointer2 = myObjectInstance::doClick2;
        pointer.onClick();
        pointer2.run();
    }
}

Então, o que temos aqui?

  1. Funcional Interface -. Esta é a interface, anotado ou não com @FunctionalInterface , que contém apenas uma declaração de método
  2. Método Referências -. Este é apenas sintaxe especial, parece que isso, ObjectInstance :: methodName , nada mais nada menos
  3. Exemplo de uso - apenas um operador de atribuição e, em seguida, interface de chamada de método
  4. .

Você deverá usar interfaces FUNCIONAIS para os ouvintes APENAS E SÓ PARA ISSO!

Como todas as outras tais ponteiros de função são realmente ruim para a legibilidade do código e para a capacidade de compreender. No entanto, referências diretas método, por vezes, vir a calhar, com foreach por exemplo.

Existem várias Interfaces funcionais predefinidas:

Runnable              -> void run( );
Supplier<T>           -> T get( );
Consumer<T>           -> void accept(T);
Predicate<T>          -> boolean test(T);
UnaryOperator<T>      -> T apply(T);
BinaryOperator<T,U,R> -> R apply(T, U);
Function<T,R>         -> R apply(T);
BiFunction<T,U,R>     -> R apply(T, U);
//... and some more of it ...
Callable<V>           -> V call() throws Exception;
Readable              -> int read(CharBuffer) throws IOException;
AutoCloseable         -> void close() throws Exception;
Iterable<T>           -> Iterator<T> iterator();
Comparable<T>         -> int compareTo(T);
Comparator<T>         -> int compare(T,T);

Para versões anteriores do Java você deve tentar Goiaba Bibliotecas, que tem funcionalidade semelhante, e sintaxe, como Adrian Petrescu foi mencionado acima.

Para olhar pesquisa adicional em Java 8 Cheatsheet

E, graças à cara com o chapéu para o linguagem Java Specification §15.13 link.

resposta

@ do sblundy é grande, mas classes internas anônimas têm duas pequenas falhas, o ser primordial que eles não tendem a ser reutilizável e o secundário é uma sintaxe volumoso.

O bom é que seus expande padrão em classes completos sem qualquer alteração na classe principal (a realizar os cálculos).

Quando você criar uma nova classe você pode passar parâmetros para que a classe que pode atuar como constantes na sua equação - por isso, se um de seus classes internas parecido com este:

f(x,y)=x*y

mas às vezes você precisa de um que é:

f(x,y)=x*y*2

e talvez uma terceira que é:

f(x,y)=x*y/2

ao invés de fazer duas classes internas anônimas ou adicionando um parâmetro "passagem", você pode fazer uma única classe real que você instancia como:

InnerFunc f=new InnerFunc(1.0);// for the first
calculateUsing(f);
f=new InnerFunc(2.0);// for the second
calculateUsing(f);
f=new InnerFunc(0.5);// for the third
calculateUsing(f);

Seria simplesmente armazenar a constante na classe e usá-lo no método especificado na interface.

Na verdade, se sabe que a sua função não será armazenada / reutilizados, você poderia fazer isso:

InnerFunc f=new InnerFunc(1.0);// for the first
calculateUsing(f);
f.setConstant(2.0);
calculateUsing(f);
f.setConstant(0.5);
calculateUsing(f);

Mas as classes imutáveis ??são mais seguros - Eu não pode vir até com uma justificação para fazer uma classe como este mutável

.

Eu realmente só postar isso porque eu tremo quando ouço classe interna anônima - eu tenho visto um monte de código redundante que era "necessário", porque a primeira coisa que o programador fez foi ir anônimo quando ele deveria ter usado um real classe e nunca repensado sua decisão.

O Google Goiaba bibliotecas , que estão se tornando muito popular, tem um genérico Função e predicado objeto que eles têm trabalhado em muitas partes de sua API.

soa como um padrão de estratégia para mim. Confira fluffycat.com Java padrões.

Uma das coisas que eu realmente sinto falta ao programar em Java é retornos de chamada de função. Uma situação em que a necessidade de estes continuou a apresentar-se foi no processamento de forma recursiva hierarquias em que deseja executar alguma ação específica para cada item. Como andar uma árvore de diretórios, ou o processamento de uma estrutura de dados. O minimalista dentro de mim odeia ter que definir uma interface e, em seguida, uma implementação para cada caso específico.

Um dia eu encontrei-me porque não me perguntando? Temos ponteiros método - o objeto Method. Com a otimização compiladores JIT, invocação reflexivo realmente não realizar mais uma penalidade de desempenho enorme. E, além disso ao lado, por exemplo, copiar um arquivo de um local para outro, o custo dos empalidece método de invocação refletidas na insignificância.

Como eu pensei mais sobre isso, percebi que uma chamada de retorno no paradigma OOP requer ligação um objeto e um método juntos -. Inserir o objeto de retorno de chamada

Confira a minha solução com base de reflexão para Callbacks em Java . Livre para qualquer uso.

Ok, esta discussão já é suficiente idade, então muito provavelmente minha resposta não é útil para a pergunta. Mas desde que esta discussão ajudou-me a encontrar a minha solução, eu vou colocá-lo aqui de qualquer maneira.

I necessário utilizar um método estático variável com entrada conhecido e de saída conhecidos (tanto duplo ). Assim, pois, sabendo que o pacote de método e nome, eu poderia funcionar da seguinte forma:

java.lang.reflect.Method Function = Class.forName(String classPath).getMethod(String method, Class[] params);

para uma função que aceita um duplo como um parâmetro.

Então, na minha situação concreta I inicializada com

java.lang.reflect.Method Function = Class.forName("be.qan.NN.ActivationFunctions").getMethod("sigmoid", double.class);

e invocou-lo mais tarde em uma situação mais complexa com

return (java.lang.Double)this.Function.invoke(null, args);

java.lang.Object[] args = new java.lang.Object[] {activity};
someOtherFunction() + 234 + (java.lang.Double)Function.invoke(null, args);

onde a atividade é um duplo valor arbitrário. Eu estou pensando em talvez fazer isto um pouco mais abstrato e generalizando, como SoftwareMonkey tem feito, mas atualmente estou bastante feliz com a maneira que é. Três linhas de código, há classes e interfaces necessárias, isso não é muito ruim.

Para fazer a mesma coisa sem as interfaces para uma variedade de funções:

class NameFuncPair
{
    public String name;                // name each func
    void   f(String x) {}              // stub gets overridden
    public NameFuncPair(String myName) { this.name = myName; }
}

public class ArrayOfFunctions
{
    public static void main(String[] args)
    {
        final A a = new A();
        final B b = new B();

        NameFuncPair[] fArray = new NameFuncPair[]
        {
            new NameFuncPair("A") { @Override void f(String x) { a.g(x); } },
            new NameFuncPair("B") { @Override void f(String x) { b.h(x); } },
        };

        // Go through the whole func list and run the func named "B"
        for (NameFuncPair fInstance : fArray)
        {
            if (fInstance.name.equals("B"))
            {
                fInstance.f(fInstance.name + "(some args)");
            }
        }
    }
}

class A { void g(String args) { System.out.println(args); } }
class B { void h(String args) { System.out.println(args); } }

Confira lambdaj

http://code.google.com/p/lambdaj/

e em particular a sua nova função de fechamento

http://code.google.com/p/lambdaj/wiki/Closures

e você vai encontrar uma maneira muito legível para definir o fechamento ou função ponteiro sem criar interface de sentido ou usar classes internas feias

Wow, porque não basta criar uma classe delegado que não é tão difícil, uma vez que eu já fiz para java e usá-lo para passar no parâmetro onde T é tipo de retorno. Lamento, mas como um # programador C ++ / C em java apenas aprender geral, eu preciso de ponteiros de função, porque eles são muito útil. Se você está familiarizado com qualquer classe que lida com o Método As informações que você pode fazê-lo. Em bibliotecas Java que seria java.lang.reflect.Method.

Se você sempre usa uma interface, você sempre tem que implementá-lo. Em eventhandling não há realmente uma maneira melhor em torno registrar / cancelar o registro da lista de manipuladores, mas para os delegados que você precisa para passar em funções e não o tipo de valor, fazendo uma classe delegado para lidar com isso para outclasses uma interface.

Nenhum dos Java 8 respostas têm dado um completo exemplo, coesa, então aqui se trata.

Declare o método que aceita o "ponteiro de função" da seguinte forma:

void doCalculation(Function<Integer, String> calculation, int parameter) {
    final String result = calculation.apply(parameter);
}

Chamá-lo, fornecendo a função com uma expressão lambda:

doCalculation((i) -> i.toString(), 2);

Se alguém está lutando para passar uma função que leva um conjunto de parâmetros para definir o seu comportamento, mas um outro conjunto de parâmetros em que para executar, como Esquema de:

(define (function scalar1 scalar2)
  (lambda (x) (* x scalar1 scalar2)))

veja Função Pass com Comportamento Parameter-Definido em Java

Desde Java8, você pode usar lambdas, que também têm bibliotecas do oficial SE 8 API.

Uso: Você precisa usar uma interface com apenas um método abstrato. Faça uma instância dele (você pode querer usar o Java SE 8 já fornecidas) como este:

Function<InputType, OutputType> functionname = (inputvariablename) {
... 
return outputinstance;
}

Para mais informações checkout da documentação: https://docs.oracle .com / JavaSE / tutorial / java / javaOO / lambdaexpressions.html

Antes de Java 8, substituto mais próximo para a funcionalidade da função-ponteiro-como era uma classe anônima. Por exemplo:

Collections.sort(list, new Comparator<CustomClass>(){
    public int compare(CustomClass a, CustomClass b)
    {
        // Logic to compare objects of class CustomClass which returns int as per contract.
    }
});

Mas agora, em Java 8 temos uma alternativa muito puro conhecido como lambda expressão, que pode ser usado como:

list.sort((a, b) ->  { a.isBiggerThan(b) } );

onde isBiggerThan é um método em CustomClass. Podemos também usar as referências de métodos aqui:

list.sort(MyClass::isBiggerThan);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top